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

  1. Retry after the transaction is fully confirmed
  2. Fetch the receipt yourself once and confirm transaction_index is populated before generating the proof
  3. 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

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


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