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

# Chain Reorgs and Nonce Recovery

> What happens when a source-chain reorg runs deeper than your configured confirmation depth, which messages recover on their own, which become permanently undeliverable, and how to respond.

[Confirmation depth](/v2/concepts/modular-security/production-dvn-configuration#confirmation-depth) sets how many source-chain blocks a DVN waits before it attests to a message. It makes a reorg unlikely to invalidate a message the destination has already accepted. It does not make it impossible.

This page covers what happens when a reorg runs deeper than the confirmation depth you configured. Many messages in that situation recover with no action from you. A smaller set becomes permanently undeliverable, and no endpoint function can revive them.

## How a reorg breaks nonce agreement

Each pathway keeps a nonce on both chains. The source endpoint increments `outboundNonce` on every send. The destination endpoint tracks `lazyInboundNonce`, the last nonce it has settled, and stores a payload hash for each verified nonce.

A deep reorg breaks the agreement between the two:

1. Your OApp sends a message on the source chain. The endpoint increments `outboundNonce` to `N`.
2. The DVNs wait for your configured confirmations, then attest. The destination verifies nonce `N`, and the executor delivers it.
3. The source chain reorgs past the block that held the send. That block is no longer canonical, so the chain discards the storage write and `outboundNonce` falls back to `N-1`.
4. The next send on the source chain takes nonce `N` again.

The destination has already consumed `N`. The source is about to reuse it. What happens to that replacement message depends entirely on how far the destination got before the reorg.

## Recovery depends on execution, not verification

The destination endpoint decides whether a nonce can be verified again with one rule:

```solidity wrap theme={null}
function _verifiable(
    Origin calldata _origin,
    address _receiver,
    uint64 _lazyInboundNonce
) internal view returns (bool) {
    return
        _origin.nonce > _lazyInboundNonce || // either initializing an empty slot or reverifying
        inboundPayloadHash[_receiver][_origin.srcEid][_origin.sender][_origin.nonce] != EMPTY_PAYLOAD_HASH;
}
```

Aptos, Sui, and Initia apply the same rule. Solana reaches the same outcome through a different mechanism. Executing a message closes its payload hash account, and the `init_verify` instruction requires the nonce to be greater than the `inbound_nonce` held on the pathway's `Nonce` account. A consumed nonce therefore has no account left to re-verify and cannot be opened again.

### Verified but not executed: the message recovers

If the destination verified nonce `N` but no one executed it, the payload hash is still stored and `lazyInboundNonce` has not moved. The second clause holds, so the replacement message can verify.

Verifying it overwrites the stored payload hash. The orphaned message becomes unexecutable and the replacement executes in its place. The pathway delivers exactly one message for nonce `N` and loses nothing. This is the ordinary re-verification path, and it needs no intervention.

### Executed, cleared, or skipped: the message is lost

If the destination executed nonce `N`, `lazyInboundNonce` is at or above `N` and execution deleted the payload hash. Both clauses fail, so `verify` reverts with `LZ_PathNotVerifiable`.

This state is permanent. There is no timeout after which the message becomes deliverable, and no number of retries changes the outcome. Calling `clear` or `skip` on a nonce leaves it in the same dead state, because both advance `lazyInboundNonce` past it.

## Replacement messages reuse the GUID

A GUID is derived from the nonce and the pathway, with no input from the payload or the block:

```solidity wrap theme={null}
keccak256(abi.encodePacked(_nonce, _srcEid, _sender.toBytes32(), _dstEid, _receiver))
```

A replacement message that reuses nonce `N` therefore carries the same GUID as the message the reorg removed, even though its contents differ. If your off-chain accounting treats the GUID as a unique message identifier, it will merge two different messages into one record. Key your replay protection and reconciliation on something other than the GUID, because GUID uniqueness does not survive a reorg.

## How many messages you lose

Only the reused nonces fail. Once `outboundNonce` climbs back above the destination's `lazyInboundNonce`, messages verify and deliver normally again.

Once the reorg settles, the destination counter is ahead of the source counter. The distance between them is the number of undeliverable sends:

```text theme={null}
lost sends = destination lazyInboundNonce - source outboundNonce
```

If the source counter is the higher of the two, the pathway has not diverged and nothing is lost.

Suppose the destination executed nonces 1 through 3 and the reorg returned `outboundNonce` to 2. The gap is `3 - 2 = 1`. The next send takes nonce 3 and is lost. The send after that takes nonce 4 and delivers normally.

## Detect the divergence

Nothing surfaces this automatically, so monitor for it.

Read `outboundNonce(sender, dstEid, receiver)` on the source endpoint and `lazyInboundNonce(receiver, srcEid, sender)` on the destination endpoint for each pathway you operate. In normal operation the source counter is at or above the destination counter. If the source counter falls below the destination counter, the pathway has diverged and every send until it catches up will fail to verify.

Also alert on `LZ_PathNotVerifiable` reverts from `verify` on the destination. A healthy pathway that starts producing them has diverged.

## Respond to a reorg

1. Stop sending on the affected pathway. Every send while the source counter sits below the destination counter debits the sender and cannot be delivered.
2. Record the gap between the two counters. That is how many sends are unrecoverable.
3. Reconcile the message that executed before the reorg. The destination applied it and the source no longer records the send, so an OFT has credited tokens on the destination with no matching debit on the source.
4. Advance `outboundNonce` past the destination's `lazyInboundNonce` before you resume real traffic.
   * Sending is the only way to do this. The endpoint increments `outboundNonce` inside `_outbound`, which only `send` calls, and it exposes no function to set the counter directly.
   * Every send you use to close the gap pays fees and is never delivered.
   * If the gap is large, deploying a new OApp on either side gives you a fresh channel, because the nonce is keyed by the sender and receiver addresses.
5. Re-issue the lost payloads as new messages once the counters agree.

## Prevent it

Set `confirmations` above the worst reorg depth the source chain has produced, not its typical depth, and pin the value explicitly on both sides of the pathway. The per-chain floors are in [Confirmation depth](/v2/concepts/modular-security/production-dvn-configuration#confirmation-depth). Nothing you do after the fact substitutes for this, because the stranded messages cannot be recovered.

Two design choices reduce what a single reorg can cost you. Per-message or per-window transfer caps bound the value that one incident can strand. A pause on the pathway lets you stop debiting users as soon as you detect the divergence, which is what keeps the loss from growing while you investigate.

For high-value pathways, note that the damage becomes permanent only at execution. A pathway that delays or gates destination execution, for example by requiring an operator to confirm before the executor runs, stays in the recoverable case for longer.
