linera-io/linera-protocol · warning · ProofError
block not found for hash {block_hash}
Error message
block not found for hash {block_hash} What it means
eth_getBlockByHash returned null for the block hash taken from the receipt. Classified ProofError::Transient. The RPC node does not know this block: most commonly the block was pruned (node keeps limited history), is on a reorged-out branch, or the node is not synced to that height.
Source
Thrown at linera-bridge/src/proof/gen.rs:120
"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 {
return Err(ProofError::Permanent(anyhow::anyhow!(
"header RLP hash mismatch: computed {computed_hash}, expected {block_hash}. \
This may indicate the RPC returned non-standard header fields."
)));
}
// 3. Get all block receipts
let all_receipts = self
.provider
.get_block_receipts(block.header.number.into())View on GitHub (pinned to 6c226ddcb3)
Solutions
- Use an archive or full-history RPC endpoint that serves the deposit's block
- Generate proofs for recent transactions before they age out of the node's history
- Retry once to rule out transient routing; if consistently null, the history is pruned
- Verify the block hash on an explorer and compare with the block number the RPC can serve
Defensive patterns
Strategy: retry
Validate before calling
// Preflight: check the RPC can serve the block for this receipt.
let receipt = provider.get_transaction_receipt(tx_hash).await?.expect("mined");
let bh = receipt.inner.block_hash.expect("confirmed");
if provider.get_block_by_hash(bh).await.ok().flatten().is_none() {
// history pruned: switch to an archive RPC before generating proofs
} Try / catch
Err(ProofError::Transient(e)) if e.to_string().contains("block not found") => {
// once retried: if still failing, history is pruned — move to an archive endpoint
} Prevention
- Use archive/full-history RPC endpoints for deposit proof generation
- Process deposits while they are recent, before history pruning windows expire
- Monitor for this error to detect when your RPC's retention is too short
When it happens
Trigger: Calling generate_deposit_proof for an older deposit against a pruned node (public/free Base endpoints often keep only recent blocks); a reorg between fetching the receipt and fetching the block; an archive gap on a load-balanced cluster.
Common situations: Free or default RPC endpoints with short block-history retention; batch-processing historical deposits; RPC providers that route eth_getBlockByHash to nodes with different prunes.
Related errors
- block receipts not found for block {block_hash}
- transaction receipt not found for {tx_hash}
- receipt missing block_hash (pending tx?)
- receipt missing transaction_index
- 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/4cc464c9fd351d45.
Report an issue: GitHub.