linera-io/linera-protocol · warning · ProofError

transaction receipt not found for {tx_hash}

Error message

transaction receipt not found for {tx_hash}

What it means

In HttpDepositProofClient::generate_deposit_proof, eth_getTransactionReceipt succeeded but returned null. It is wrapped as ProofError::Transient, meaning a retry may succeed — the typical cause is a transaction that is not mined or not yet indexed by the RPC node, or a hash that does not exist on this chain.

Source

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

            .with_context(|| format!("invalid RPC URL: {rpc_url}"))?;
        let provider = ProviderBuilder::<_, _, Optimism>::default().connect_http(url);
        Ok(Self {
            provider: Box::new(provider),
        })
    }
}

#[async_trait]
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(|| {

View on GitHub (pinned to 6c226ddcb3)

Solutions

  1. Wait until the transaction is mined (1-2 confirmations) and retry
  2. Verify the tx hash exists on that exact network using a block explorer for the same chain
  3. Check --rpc-url matches the chain the deposit was sent to (e.g. Base L2)
  4. If it still fails after finality, the hash is wrong or the tx was dropped — stop retrying
Defensive patterns

Strategy: retry

Validate before calling

// Preflight: only attempt a proof once the receipt is mined and indexed.
loop {
    let r = provider.get_transaction_receipt(tx_hash).await?;
    if let Some(r) = r.filter(|r| r.inner.block_hash.is_some() && r.inner.transaction_index.is_some()) {
        break;
    }
    tokio::time::sleep(std::time::Duration::from_secs(5)).await;
}

Try / catch

match client.generate_deposit_proof(tx_hash).await {
    Ok(p) => { /* submit ProcessDeposit */ }
    Err(ProofError::Transient(e)) => { /* schedule retry with backoff */ }
    Err(ProofError::Permanent(e)) => { /* log, alert, do not retry */ }
}

Prevention

When it happens

Trigger: Calling generate_deposit_proof(tx_hash) immediately after broadcasting the deposit transaction; --rpc-url pointing at a different chain than the one the tx was sent to; a mistyped or dropped transaction hash; an RPC node whose indexer lags behind block production.

Common situations: Public/free RPC endpoints with indexing lag; mixing up networks (Ethereum mainnet RPC with a Base tx hash); tx still in the mempool or dropped due to low gas; automations that generate proofs without waiting for confirmations.

Related errors


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