{"record":{"id":"ee5218c313ab1be3","repo":"linera-io/linera-protocol","slug":"failed-to-check-block-finality-block-may-not-exi","errorCode":null,"errorMessage":"failed to check block finality — block may not exist","messagePattern":"failed to check block finality — block may not exist","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"linera-bridge/contracts/evm-bridge/src/service.rs","lineNumber":224,"sourceCode":"    ///\n    /// Makes the EVM JSON-RPC calls in the service runtime so that the contract\n    /// sees a single deterministic oracle response (the boolean result) instead\n    /// of multiple raw HTTP responses with non-deterministic headers.\n    async fn is_block_hash_finalized(&self, block_hash: String) -> bool {\n        let bytes: [u8; 32] = hex::decode(block_hash.strip_prefix(\"0x\").unwrap_or(&block_hash))\n            .expect(\"invalid hex\")\n            .try_into()\n            .expect(\"hash must be 32 bytes\");\n        let rpc_endpoint = self.state.rpc_endpoint.get().clone();\n        assert!(\n            !rpc_endpoint.is_empty(),\n            \"rpc_endpoint must be configured to verify block hashes\"\n        );\n        let client = ServiceEthereumClient::new(rpc_endpoint);\n        client\n            .is_block_hash_finalized(B256::from(bytes))\n            .await\n            .expect(\"failed to check block finality — block may not exist\")\n    }\n}\n","sourceCodeStart":206,"sourceCodeEnd":227,"githubUrl":"https://github.com/linera-io/linera-protocol/blob/6c226ddcb332ef55118dc8d0aafbd093d5420899/linera-bridge/contracts/evm-bridge/src/service.rs#L206-L227","documentation":"is_block_hash_finalized in the service calls ServiceEthereumClient::is_block_hash_finalized against the configured RPC endpoint and expects success. The panic means the RPC interaction failed — endpoint unreachable, rate-limited, erroring, or the block genuinely not found/not yet finalized — and it propagates as a GraphQL error to the contract's VerifyBlockHash oracle, which then also aborts. This is the service-side twin of the contract's finality check.","triggerScenarios":"Calling VerifyBlockHash while the rpc_endpoint is down or rate-limiting; querying a block hash that does not exist on the endpoint (wrong network, pruned, or not yet known); transient network partitions between validators and the RPC provider; provider returning 429/5xx during load spikes.","commonSituations":"RPC provider incidents or quota exhaustion during high bridge traffic; querying finality of very recent blocks the provider has not indexed; misconfigured endpoint pointing at a different network than the block belongs to.","solutions":["Check provider health first: a simple eth_blockNumber call to the same endpoint","If the block is recent, wait for finality (2 epochs on Ethereum PoS) and retry VerifyBlockHash","Verify the endpoint still reports the bridge's source_chain_id (chain mismatch makes blocks 'not exist')","For production, use a redundant/paid RPC tier or a local node to avoid rate limits","Treat repeated failures as an operations incident: deposits depending on the oracle will stall"],"exampleFix":"# before\nlinera query-service ... 'query { isBlockHashFinalized(blockHash: \"0x...\") }'\n# -> error: failed to check block finality — block may not exist\n\n# after — triage then retry\ncurl -s $RPC -X POST -H 'content-type: application/json' \\\n  -d '{\"jsonrpc\":\"2.0\",\"method\":\"eth_getBlockByHash\",\"params\":[\"0x...\",false],\"id\":1}'\n# null result -> wrong network/pruned/not known yet: fix endpoint or wait for finality\n# block present -> transient RPC fault: retry the query","handlingStrategy":"retry","validationCode":"// Triage before retrying the finality query:\nasync fn rpc_alive(url: &str) -> bool {\n    reqwest::Client::new().post(url).json(&serde_json::json!({\n        \"jsonrpc\": \"2.0\", \"method\": \"eth_blockNumber\", \"params\": [], \"id\": 1\n    })).send().await.map(|r| r.status().is_success()).unwrap_or(false)\n}","typeGuard":null,"tryCatchPattern":"// Retry with backoff at the caller; the service query itself either returns\n// true/false or errors:\nfor attempt in 0..4 {\n    match query_finality(&block_hash).await {\n        Ok(v) => return Ok(v),\n        Err(_) if attempt < 3 => tokio::time::sleep(Duration::from_secs(2u64 << attempt))).await,\n        Err(e) => return Err(e),\n    }\n}","preventionTips":["Use an RPC tier with uptime guarantees or run a local node for the bridge","Only query finality for blocks at least two epochs old","Alert when VerifyBlockHash keeps failing — downstream deposits will stall"],"tags":["linera","bridge","json-rpc","finality","block-not-found","network","panic"],"backgroundTag":"rpc-endpoint-unreachable","analyzedSha":"6c226ddcb332ef55118dc8d0aafbd093d5420899","analyzedAt":"2026-08-22T22:49:09.787Z","schemaVersion":2},"datasetVersion":"2026-08-23T01:17:44.959Z"}