linera-io/linera-protocol · warning · ProofError

block receipts not found for block {block_hash}

Error message

block receipts not found for block {block_hash}

What it means

eth_getBlockReceipts(block_number) returned null. Classified ProofError::Transient. The endpoint either cannot serve receipts for that block (pruned history) or does not fully support the method for it. The proof pipeline needs all receipts of the block to rebuild the receipts trie, so it aborts.

Source

Thrown at linera-bridge/src/proof/gen.rs:142

        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())
            .await
            .map_err(|e| ProofError::Transient(e.into()))?
            .ok_or_else(|| {
                ProofError::Transient(anyhow::anyhow!(
                    "block receipts not found for block {block_hash}"
                ))
            })?;

        // 4. Encode each receipt to canonical EIP-2718 form (as stored in the trie).
        //    Convert RPC logs → primitives logs so Encodable2718 is available.
        //    OpReceiptEnvelope handles both standard types and OP deposit (0x7e).
        let canonical_receipts: Vec<(u64, Vec<u8>)> = all_receipts
            .into_iter()
            .enumerate()
            .map(|(idx, r)| {
                let consensus_receipt = r.inner.inner.map_logs(|log| log.inner);
                let encoded = consensus_receipt.encoded_2718();
                (idx as u64, encoded)
            })
            .collect();

        // 5. Build receipt trie and generate proof

View on GitHub (pinned to 6c226ddcb3)

Solutions

  1. Use an archive node or full-history provider
  2. Retry shortly after block production to rule out indexer lag
  3. Confirm the provider supports eth_getBlockReceipts by querying the block number directly
Defensive patterns

Strategy: retry

Validate before calling

// Preflight: confirm eth_getBlockReceipts is servable for the block.
let block = provider.get_block_by_hash(bh).await?.expect("block");
if provider.get_block_receipts(block.header.number.into()).await.ok().flatten().is_none() {
    // provider cannot serve receipts: use an archive RPC
}

Try / catch

Err(ProofError::Transient(e)) if e.to_string().contains("block receipts not found") => {
    // short backoff first (indexer lag), then fall back to an archive provider
}

Prevention

When it happens

Trigger: Calling generate_deposit_proof for an old block on a node with pruned receipt history; providers that gate or partially implement eth_getBlockReceipts; racing the node indexer right after a block is produced.

Common situations: Free-tier RPC plans with history limits; batch proof generation for historical deposits; some self-hosted nodes started without archive flags.

Related errors


AI-assisted analysis of linera-io/linera-protocol@6c226ddcb3 (2026-08-22). Data as JSON: /api/errors/e3505910c95ebff4. Report an issue: GitHub.