{"record":{"id":"4c2bec19b97a41aa","repo":"linera-io/linera-protocol","slug":"receipt-missing-transaction-index","errorCode":null,"errorMessage":"receipt missing transaction_index","messagePattern":"receipt missing transaction_index","errorType":"exception","errorClass":"ProofError","httpStatus":null,"severity":"warning","filePath":"linera-bridge/src/proof/gen.rs","lineNumber":110,"sourceCode":"impl DepositProofClient for HttpDepositProofClient {\n    async fn generate_deposit_proof(&self, tx_hash: B256) -> Result<DepositProof, ProofError> {\n        // 1. Get transaction receipt → block hash, tx index\n        let receipt = self\n            .provider\n            .get_transaction_receipt(tx_hash)\n            .await\n            .map_err(|e| ProofError::Transient(e.into()))?\n            .ok_or_else(|| {\n                ProofError::Transient(anyhow::anyhow!(\n                    \"transaction receipt not found for {tx_hash}\"\n                ))\n            })?;\n\n        let block_hash = receipt.inner.block_hash.ok_or_else(|| {\n            ProofError::Transient(anyhow::anyhow!(\"receipt missing block_hash (pending tx?)\"))\n        })?;\n        let tx_index = receipt.inner.transaction_index.ok_or_else(|| {\n            ProofError::Transient(anyhow::anyhow!(\"receipt missing transaction_index\"))\n        })?;\n\n        // 2. Get full block → header RLP\n        let block = self\n            .provider\n            .get_block_by_hash(block_hash)\n            .await\n            .map_err(|e| ProofError::Transient(e.into()))?\n            .ok_or_else(|| {\n                ProofError::Transient(anyhow::anyhow!(\"block not found for hash {block_hash}\"))\n            })?;\n\n        let mut block_header_rlp = Vec::new();\n        block.header.inner.encode(&mut block_header_rlp);\n\n        // Sanity check: header RLP hashes to the expected block hash\n        let computed_hash = alloy_primitives::keccak256(&block_header_rlp);\n        if computed_hash != block_hash {","sourceCodeStart":92,"sourceCodeEnd":128,"githubUrl":"https://github.com/linera-io/linera-protocol/blob/6c226ddcb332ef55118dc8d0aafbd093d5420899/linera-bridge/src/proof/gen.rs#L92-L128","documentation":"The fetched receipt has no transaction_index field. The proof generator needs the index to key the receipt into the block's receipts trie; without it the pipeline cannot proceed. Classified ProofError::Transient, matching the pending-tx case where some providers omit the index.","triggerScenarios":"Calling generate_deposit_proof on a receipt served while pending or by an endpoint that strips transaction_index; mixed responses from load-balanced RPC clusters where one backend is behind.","commonSituations":"Non-standard or partial RPC implementations (some hosted providers, proxy gateways); racing a node right after block production.","solutions":["Retry after the transaction is fully confirmed","Fetch the receipt yourself once and confirm transaction_index is populated before generating the proof","Switch to a standard, fully synced RPC provider if the field stays missing"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"let ready = provider.get_transaction_receipt(tx_hash).await?.is_some_and(|r| {\n    r.inner.transaction_index.is_some() && r.inner.block_hash.is_some()\n});\nif !ready { /* wait and retry later */ }","typeGuard":null,"tryCatchPattern":"Err(ProofError::Transient(e)) if e.to_string().contains(\"transaction_index\") => {\n    // incomplete receipt: retry; if persistent, switch provider\n}","preventionTips":["Gate proof generation on fully populated receipts","Avoid load-balanced endpoints that mix node versions during block production","Retry with backoff — the error class is Transient by design"],"tags":["evm","rpc","receipt","bridge","transient","proof"],"backgroundTag":"incomplete-rpc-response","analyzedSha":"6c226ddcb332ef55118dc8d0aafbd093d5420899","analyzedAt":"2026-08-22T22:49:09.787Z","schemaVersion":2},"datasetVersion":"2026-08-23T01:17:44.959Z"}