The one thing to understand first
Hot standby lets a streaming standby serve read-only queries while it continuously replays WAL from the primary. This is how PostgreSQL scales reads: route SELECT traffic to one or more standbys. But it creates a tension that does not exist on the primary — the standby is doing two things at once: replaying changes and answering queries against the data those changes are modifying.
A recovery conflict is fundamentally unsolvable in the moment: when replayed WAL would remove rows a standby query still needs, PostgreSQL can keep replay current or keep the query alive, never both. Every hot-standby knob is just a choice about which side loses.
Why recovery conflicts happen
The standby replays the primary’s WAL, including operations that remove data a running query still needs to see. The classic case: vacuum on the primary removes dead tuples; that removal arrives as a WAL <a class="sev1-termlink" href="https://thesev1database.com/glossary/tuple/" title="Tuple">record; replaying it would delete row versions that a long-running query on the standby is still reading under its MVCC snapshot. The replay and the query conflict over the same rows — a recovery conflict.