# Retries and reorgs

Source: https://docs.graphops.xyz/rpc/guides/retries-and-reorgs

Recover from temporary failures without duplicating work, and keep stored data correct when the
chain reorganizes. Match each retry to the kind of request, and store enough block identity to
undo data from replaced blocks.

## Prerequisites

* A retry budget for your request path.
* Logging for method, network, block reference, HTTP status, and JSON-RPC error code.
* A durable cursor for long-running log or WebSocket workflows.

## Retry guidance

| Request type             | Retry guidance                                                          |
| ------------------------ | ----------------------------------------------------------------------- |
| Current-state reads      | Retry `429`, `502`, `503`, and `504` with backoff and jitter.           |
| Historical reads         | Retry smaller ranges when a request times out or returns too much data. |
| `eth_call` simulations   | Retry transport failures; treat EVM reverts as application results.     |
| `eth_sendRawTransaction` | Retry the same signed transaction; do not mutate and resubmit blindly.  |
| Batch requests           | Retry failed items individually.                                        |
| WebSocket subscriptions  | Reconnect, resubscribe, and backfill missed blocks.                     |

For every retry:

* Use exponential backoff with jitter, and cap the total retry time.
* Log the method, network, block reference or range, HTTP status, and JSON-RPC error code.
* Pass the same explicit block number or hash when the retry must observe the same state as the
first attempt. A retried `latest` read can land on a newer block.

See [API integration](../api#error-responses) for the meaning of each status and error code.

## Handle reorgs

Recent blocks can be replaced by a chain reorganization. Logs you already processed from those
blocks no longer describe the chain, and the replacement blocks can contain different logs.

*A reorg replaces blocks N+1 and N+2. A logs subscription sends the replaced logs again with removed: true, then the logs from the new blocks. Keying logs by block hash keeps both versions apart; finalized blocks are not reorganized.*

- Before: N-1, N, N+1, N+2.
- After: N-1, N, N+1′, N+2′.
- Logs from N+1, N+2 with `"removed": true`: delete each by (blockHash, transactionHash, logIndex).
- Logs from N+1′, N+2′ with `"removed": false`: insert; the new blockHash keeps their keys distinct.

1. Key stored logs by `(blockHash, transactionHash, logIndex)`. The block hash keeps logs from a
replaced block distinct from logs in its replacement.
2. Over a WebSocket `logs` subscription, treat each log with `removed: true` as a reversal and
delete or undo the matching stored log.
3. When you poll with `eth_getLogs`, keep the block hashes of recent blocks you processed. Compare
them with `eth_getBlockByNumber` on each poll, and replay from the last block whose hash still
matches.
4. For settlement, accounting, or other irreversible steps, act only on data at or behind the
`finalized` block. Use `safe` when you can accept a small reorg risk for lower latency.

## Next steps

* [Consistency and finality](../concepts/consistency-and-finality): Choose block references that keep related reads on one state.
* [WebSocket subscriptions](./websocket-subscriptions): Recover a live stream after a disconnect.
* [Usage and limits](../usage-and-limits): Plan retry volume within your organization's shared limits.