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
- Long-running queries on the replica (reports, analytics) racing against vacuum activity on the primary — by far the usual case.
- DDL on the primary: replaying a DROP or ALTER needs an exclusive lock on the replica, conflicting with any query using the object.
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:
hot_standby_feedback = on(on the replica): the replica tells the primary which row versions it still needs, and vacuum on the primary spares them. Queries stop dying; the cost is bloat on the primary held back by long replica queries — the same effect as a long transaction running locally.- Raise
max_standby_streaming_delayon a dedicated reporting replica: queries win, the replica lags during them. Fine when the replica is not used for failover-critical freshness. - Retry on SQLSTATE 40001 for short queries — the HINT means it: a moment later the same query usually succeeds.
- Split workloads: a fast, fresh replica for app reads; a separate replica with generous delay for analytics.
