> ## Documentation Index
> Fetch the complete documentation index at: https://docs.sqd.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Blockchain forks and recovery

> How Portal reports a chain reorganization while a client reads recent blocks.

A blockchain can reorganize, or **reorg**, when blocks on the current chain are
replaced by blocks from another branch. Data derived from the replaced blocks
must also be removed or recalculated.

Portal can detect this when a client resumes a standard stream with:

* `fromBlock`: the next block the client wants
* `parentBlockHash`: the hash of the block immediately before `fromBlock`,
  meaning the last block the client already trusts

Portal compares the hash with the parent of `fromBlock` on the current chain.
If the hashes match, the stream starts normally. If they do not match, the
chain changed between the client's last processed block and `fromBlock`. The
client cannot safely resume from this block, and Portal returns HTTP `409
Conflict`.

<Warning>
  Send `parentBlockHash` whenever you resume a standard stream. A reorg can
  happen on any chain; you cannot prevent conflicts, only detect them. A
  client that omits `parentBlockHash` silently processes blocks from a forked
  chain after a reorg, with no signal that anything is wrong.
</Warning>

For example, after this reorg the block `N+1` that the client may have already
processed is no longer on the current chain:

```mermaid theme={"system"}
flowchart LR
  A["N-1"] --> B["N"]
  B --> C["N+1<br/>orphaned"]
  B --> D["N+1'<br/>current chain tip"]
```

If the client resumes with `fromBlock = N+2` and `parentBlockHash = hash(N+1)`,
Portal sees that `N+1` is not the parent of the next block on the current
chain and reports the conflict.

## Conflict response body

```json theme={"system"}
{
  "error": {
    "type": "invalid_request_error",
    "code": "base_block_mismatch",
    "message": "Base block mismatch"
  },
  "previousBlocks": [
    { "number": 21780871, "hash": "0xab12cd..." },
    { "number": 21780872, "hash": "0xf6a96a29..." }
  ]
}
```

* `error` is the envelope that every failing Portal response carries. Match on
  `error.code` (`base_block_mismatch` here) rather than on `error.message`,
  which is prose and can change. See [Error
  handling](/en/portal/introduction/error-handling) for the full contract.
* `previousBlocks` is an array of `{ number, hash }` pairs from the current
  chain at and below the conflict point. It stays at the top level, beside
  `error`.
* The array length is arbitrary, but the array always contains at least the
  parent of the requested `fromBlock`.
* The array is in ascending order: the lowest block number comes first, and
  the last entry is the most recent block at or below the conflict point.

## Recovering from a conflict

1. Walk `previousBlocks` from its last entry (the most recent block) and look
   for a block whose `(number, hash)` pair you have already processed and
   stored.
2. If you find a shared block at height `K`, remove data derived from later
   blocks and resume the stream with `fromBlock = K + 1` and
   `parentBlockHash = hash(K)`.
3. If no block in `previousBlocks` matches your records, the divergence is
   deeper than the returned slice. Open a new stream with a `fromBlock`
   further in the past and repeat the search. Pass `parentBlockHash` on every
   retry as well, so that each conflict narrows the search instead of
   repeating it.

Portal never says to rewind to one exact block. It returns a slice of the
current chain, and the client compares that slice against its own state.

The request examples and chain-specific recovery walkthroughs are in the API
guides:

<CardGroup cols={2}>
  <Card title="EVM fork recovery" icon="bolt" href="/en/portal/evm/api#handling-reorgs-near-the-chain-head">
    Handle HTTP 409 while reading recent EVM blocks.
  </Card>

  <Card title="Solana fork recovery" icon="bolt" href="/en/portal/solana/api#handling-reorgs-near-the-chain-head">
    Handle HTTP 409 while reading recent Solana blocks.
  </Card>
</CardGroup>

If recent blocks are not required, [use the finalized
stream](/en/portal/introduction/finalized-and-recent-data) instead. Finalized
data does not reorganize, so a `409` from the finalized stream only means the
stored hash is stale, for example carried over from an unfinalized block. The
recovery procedure is the same.


## Related topics

- [Fork handling](/en/sdk/pipes-sdk/solana/guides/architecture-deep-dives/fork-handling.md)
- [Migrate to 1.0](/en/sdk/pipes-sdk/solana/migration.md)
- [How an archival query is served](/en/network/introduction/how-a-query-is-served.md)
- [ClickHouse](/en/sdk/pipes-sdk/solana/guides/basic-development/targets/clickhouse.md)
- [Error handling](/en/portal/introduction/error-handling.md)
