{"record":{"id":"5c629ef6cfe81a6f","repo":"nautechsystems/nautilus_trader","slug":"finalized-block-changed-from-to-before-fi","errorCode":null,"errorMessage":"Finalized block {} changed from {} to {} before fill emission","messagePattern":"Finalized block (.+?) changed from (.+?) to (.+?) before fill emission","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"critical","filePath":"crates/adapters/blockchain/src/execution/client.rs","lineNumber":2259,"sourceCode":"            )\n        })?;\n    let event = dex.parse_swap_event_rpc(log)?;\n    let base_amount = if plan.pool.get_base_token().address == plan.pool.token0.address {\n        event.amount0\n    } else {\n        event.amount1\n    };\n    anyhow::ensure!(\n        base_amount.is_positive() && base_amount.unsigned_abs() == plan.amount_in,\n        \"Finalized Swap input {base_amount} does not match the persisted amount {}\",\n        plan.amount_in\n    );\n\n    let block = executor\n        .http_rpc_client\n        .block_by_number(included.block_number, false)\n        .await?;\n    anyhow::ensure!(\n        block.hash == included.receipt.block_hash,\n        \"Finalized block {} changed from {} to {} before fill emission\",\n        included.block_number,\n        included.receipt.block_hash,\n        block.hash\n    );\n    let timestamp_ns = block\n        .timestamp\n        .checked_mul(NANOSECONDS_IN_SECOND)\n        .ok_or_else(|| anyhow::anyhow!(\"Finalized block timestamp overflows nanoseconds\"))?;\n    let mut swap = event.to_pool_swap(\n        plan.pool.chain.clone(),\n        plan.instrument_id,\n        plan.pool.pool_identifier,\n        UnixNanos::from(timestamp_ns),\n    );\n    swap.calculate_trade_info(&plan.pool.token0, &plan.pool.token1, None)?;\n    let trade = swap","sourceCodeStart":2241,"sourceCodeEnd":2277,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/2114cf6f761429e0adb5ca9596fcd7b895b16011/crates/adapters/blockchain/src/execution/client.rs#L2241-L2277","documentation":"Post-finality reorg detector in emit_finalized_swap_fill: the client re-fetches the block by included.block_number and requires its hash to equal receipt.block_hash. Under proof-of-stake finality a finalized block hash must never change, so a mismatch means the RPC endpoint served data from a different fork (or the transaction was finalized against a non-canonical block).","triggerScenarios":"A load-balanced RPC provider routing consecutive calls to nodes on different forks or with different sync states; a node still on a pre-finality view; an actual consensus-rule violation (extremely rare); block data misparsed so block_number maps to the wrong height.","commonSituations":"Shared/public RPC endpoints behind a balancer; pointing execution at a node that lags finality; mixing an execution endpoint from one provider with state from another.","solutions":["Pin the execution client to a single, reputable, fully synced endpoint that supports the finalized tag instead of a rotating pool.","Immediately re-query the block hash from a second independent provider to decide which fork view is canonical.","Wait for the next finalized checkpoint and let reconciliation retry the record; do not emit fills from conflicting data.","If one provider persistently disagrees, drop it and investigate its health/sync status."],"exampleFix":null,"handlingStrategy":"retry","validationCode":"let b1 = rpc1.block_by_number(n, false).await?;\nlet b2 = rpc2.block_by_number(n, false).await?;\nif b1.hash != b2.hash {\n    log::error!(\"providers disagree on block {n}: {} vs {}\", b1.hash, b2.hash);\n}","typeGuard":null,"tryCatchPattern":"On this error, pause fill emission, re-verify the hash against a second independent provider, and retry at the next finalized checkpoint; never emit a fill while views disagree.","preventionTips":["Pin execution to one finality-aware, reputable endpoint instead of a rotating load balancer.","Continuously cross-check finalized block hashes between two providers and page on disagreement."],"tags":["blockchain","chain-reorg","rpc","finality"],"backgroundTag":"chain-reorg-detected","analyzedSha":"2114cf6f761429e0adb5ca9596fcd7b895b16011","analyzedAt":"2026-08-21T11:28:30.864Z","schemaVersion":2},"datasetVersion":"2026-08-22T14:17:55.899Z"}