PostgreSQL error: canceling statement due to conflict with recovery

FATAL:  terminating connection due to conflict with recovery
DETAIL:  User query might have needed to see row versions that must be removed.
HINT:  In a moment you should be able to reconnect to the database and repeat your command.

This error exists only on hot standby replicas. The replica must apply WAL from the primary to stay in sync; your query needed row versions that the incoming WAL (typically vacuum cleanup) removes. After stalling replay for max_standby_streaming_delay, the replica chose replication over your query and cancelled it.

What this error means

The replica serves reads while continuously replaying the primary's WAL — two jobs that conflict when replay wants to change or remove data a running query can still see. PostgreSQL first delays replay to let the query finish (up to max_standby_streaming_delay, default 30 s), then cancels the query. It is a deliberate trade-off between replica freshness and query survival, and every knob just moves along that axis.

Common causes

How to diagnose it

-- Sul replica: contatori dei conflitti per tipo e database
SELECT * FROM pg_stat_database_conflicts;

confl_snapshot is the vacuum-cleanup case; confl_lock points at DDL on the primary. Correlate with which workloads run on the replica at those times.

How to fix it

Pick the trade-off explicitly:

🔍 Related reading: VACUUM & bloat — hot_standby_feedback makes replica queries hold back cleanup exactly like long local transactions.