linera-io/linera-protocol · warning · ProofError
receipt missing transaction_index
Error message
receipt missing transaction_index
What it means
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.
Source
Thrown at linera-bridge/src/proof/gen.rs:110
impl DepositProofClient for HttpDepositProofClient {
async fn generate_deposit_proof(&self, tx_hash: B256) -> Result<DepositProof, ProofError> {
// 1. Get transaction receipt → block hash, tx index
let receipt = self
.provider
.get_transaction_receipt(tx_hash)
.await
.map_err(|e| ProofError::Transient(e.into()))?
.ok_or_else(|| {
ProofError::Transient(anyhow::anyhow!(
"transaction receipt not found for {tx_hash}"
))
})?;
let block_hash = receipt.inner.block_hash.ok_or_else(|| {
ProofError::Transient(anyhow::anyhow!("receipt missing block_hash (pending tx?)"))
})?;
let tx_index = receipt.inner.transaction_index.ok_or_else(|| {
ProofError::Transient(anyhow::anyhow!("receipt missing transaction_index"))
})?;
// 2. Get full block → header RLP
let block = self
.provider
.get_block_by_hash(block_hash)
.await
.map_err(|e| ProofError::Transient(e.into()))?
.ok_or_else(|| {
ProofError::Transient(anyhow::anyhow!("block not found for hash {block_hash}"))
})?;
let mut block_header_rlp = Vec::new();
block.header.inner.encode(&mut block_header_rlp);
// Sanity check: header RLP hashes to the expected block hash
let computed_hash = alloy_primitives::keccak256(&block_header_rlp);
if computed_hash != block_hash {View on GitHub (pinned to 6c226ddcb3)
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
Defensive patterns
Strategy: retry
Validate before calling
let ready = provider.get_transaction_receipt(tx_hash).await?.is_some_and(|r| {
r.inner.transaction_index.is_some() && r.inner.block_hash.is_some()
});
if !ready { /* wait and retry later */ } Try / catch
Err(ProofError::Transient(e)) if e.to_string().contains("transaction_index") => {
// incomplete receipt: retry; if persistent, switch provider
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: Non-standard or partial RPC implementations (some hosted providers, proxy gateways); racing a node right after block production.
Related errors
- transaction receipt not found for {tx_hash}
- receipt missing block_hash (pending tx?)
- block not found for hash {block_hash}
- block receipts not found for block {block_hash}
- header RLP hash mismatch: computed {computed_hash}, expected
AI-assisted analysis of linera-io/linera-protocol@6c226ddcb3 (2026-08-22).
Data as JSON: /api/errors/4c2bec19b97a41aa.
Report an issue: GitHub.