Fix PostgreSQL Canceling Statement Due to Conflict With Recovery
Learn why standby queries can be canceled by PostgreSQL recovery conflicts, how to diagnose the cause, and what to change safely in managed PostgreSQL.
The PostgreSQL error canceling statement due to conflict with recovery usually appears on a read replica, not on the primary database. The query may be perfectly valid. PostgreSQL cancels it because the standby has to keep replaying write-ahead log records from the primary, and the query is holding onto a snapshot or resource that blocks that recovery work.
The fastest fix is to identify whether the query belongs on the replica at all, then either shorten the query, route it to the primary, or tune standby-conflict behavior deliberately. Raising delays without understanding the workload can make replicas stale, while ignoring the error can break dashboards, analytics jobs, and read-after-write paths in production.
Why this error happens
A PostgreSQL standby is not just a second server answering reads. In physical streaming replication, it continuously receives and replays WAL from the primary. That replay may need to remove dead row versions, apply DDL, drop files, or take locks that are incompatible with a query currently running on the standby. When the standby cannot both keep replaying recovery and let the query continue, PostgreSQL eventually cancels the query.
The official hot standby documentation calls these standby query conflicts. They can be caused by locks, dropped tablespaces, dropped databases, vacuum cleanup, buffer pin conflicts, or deadlocks detected while replaying WAL. The user-facing message often looks the same at first glance, so the useful diagnosis is not just "the replica is broken." It is "which recovery operation did this read block, and is the read allowed to lag recovery?"
This is most visible on replicas used for reporting, admin queries, or expensive API reads. A query that scans many rows can keep an old snapshot alive. Meanwhile, the primary keeps updating and vacuuming. The standby cannot retain every old row version forever, so it chooses between canceling the read and falling behind the primary.
Quick diagnosis
Start by confirming that the failing connection is pointed at a standby. If the application has separate read and write URLs, check the exact connection string used by the failing job or request. In a SQL session, pg_is_in_recovery() returns true on a standby.
Then look for two clues: long-running standby queries and replica lag. On the standby, pg_stat_activity shows queries with old start times. PostgreSQL also exposes WAL receive and replay positions through functions such as pg_last_wal_receive_lsn() and pg_last_wal_replay_lsn(). On the primary, pg_stat_replication helps show whether the standby is behind at the write, flush, or replay stage.
| Symptom | Likely cause | Safer first response |
|---|---|---|
| Large report fails on a read replica | Query snapshot conflicts with WAL replay or cleanup | Shorten the report, paginate it, or run it on a dedicated analytics path |
| Error appears after a migration | Standby replay needs locks for DDL from the primary | Avoid routing schema-sensitive reads to replicas during migrations |
| Replica lag grows when conflicts stop happening | Delay settings or feedback may be protecting queries too much | Revisit freshness requirements before increasing standby delays further |
| API read fails soon after a write | Read-after-write traffic is routed to a stale or recovering replica | Route that request path to the primary or use explicit consistency rules |
| Conflicts mention vacuum cleanup | Standby snapshot needs row versions the primary vacuum wants removed | Shorten standby transactions; consider feedback only with bloat monitoring |
Fix the query path first
For most application incidents, the best first fix is not a server setting. It is routing and query shape. If a request must see data immediately after a write, send it to the primary. Read replicas are excellent for tolerant reads, background exports, and dashboards, but they are a poor default for user flows that require fresh results after a mutation.
If the query is a report, make it less hostile to recovery. Add selective predicates, paginate by a stable key, avoid open-ended exports during peak write periods, and make sure the query uses appropriate indexes. A five-minute sequential scan on a hot table is much more likely to conflict than a focused query that returns the same business answer in seconds.
For migrations, reduce surprise by treating replicas as temporarily inconsistent readers. DDL on the primary can create recovery conflicts on standbys. During a migration window, route admin screens, jobs, and schema-introspecting tools carefully. If a deploy runs a migration and a long standby query simultaneously, the cancellation may be the replica protecting recovery rather than a random failure.
When settings are involved
PostgreSQL has standby delay settings, including max_standby_streaming_delay, that control how long a standby may wait before canceling conflicting queries while applying streamed WAL. Increasing the delay can help long reads finish, but it directly trades off replica freshness. A replica that waits longer may serve older data for longer.
Another setting, hot_standby_feedback, lets a standby tell the primary about queries that need old row versions. That can reduce some cleanup conflicts, but it can also prevent vacuum from removing dead tuples on the primary, increasing table and index bloat if long standby transactions are common. It is a real operational tradeoff, not a harmless switch.
In managed PostgreSQL, you may not control every parameter directly, and that is often a good thing. Before asking a provider to change replica-conflict settings, write down the workload goal: fresher replicas, fewer canceled reports, or safer read-after-write behavior. Those goals are different. ArmorDB users should pair replica reads with normal connection-pool discipline and keep consistency-sensitive code paths on the primary unless the application explicitly tolerates lag.
Validate the fix
After changing routing, query shape, or settings, test the same workload from the same runtime that failed. Confirm that the query completes, that replica lag remains within the application's tolerance, and that the primary is not accumulating avoidable bloat because standby reads stay open too long. A successful fix is not only "the error disappeared." It is that the replica can keep replaying WAL while the application gets the consistency level it expects.
Add a short runbook entry that names which services may use replicas, which must use the primary, and what lag or cancellation alerts mean. Without that distinction, teams often rediscover the same conflict during every large report, migration, or customer import.
Takeaway
canceling statement due to conflict with recovery means a standby read got in the way of replaying primary changes. Fix it by matching reads to their consistency needs, shortening expensive replica queries, and tuning standby conflict settings only after deciding how much lag is acceptable. The safest production posture is to reserve replicas for reads that can tolerate delay and keep critical read-after-write paths on the primary.
Sources and further reading
Topic
Short-Form & Quick Fixes
Updated
Jul 7, 2026
Read time
5 min read
ArmorDB Engineering writes about PostgreSQL operations, security, and infrastructure decisions for teams building production apps on ArmorDB.
Read next
Deep Dives · 9 min read
PostgreSQL Autovacuum and Bloat in Managed Databases: A Practical Guide
Learn how PostgreSQL autovacuum controls dead rows and table bloat, which signals to watch, and how managed PostgreSQL teams can prevent maintenance surprises.
Read articleTech-News & Trends · 6 min read
PostgreSQL 18 MD5 Password Deprecation: How to Move to SCRAM
PostgreSQL 18 deprecates MD5-encrypted passwords; here is how to audit roles, move to SCRAM-SHA-256, and avoid surprise login failures.
Read article