{"record":{"id":"55b22dab55508e51","repo":"linera-io/linera-protocol","slug":"transaction-tx-hash-not-confirmed-within-receip","errorCode":null,"errorMessage":"transaction {tx_hash} not confirmed within {RECEIPT_TIMEOUT:?}","messagePattern":"transaction (.+?) not confirmed within (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"linera-bridge/src/relay/evm.rs","lineNumber":89,"sourceCode":"    /// `getBlockByNumber` and one cheap call per interval, only while a tx is in\n    /// flight (nothing when idle). Returns an error if the tx reverted (status\n    /// 0), so a failed settlement is retried rather than treated as confirmed.\n    async fn await_receipt(&self, tx_hash: B256) -> Result<TransactionReceipt> {\n        let deadline = Instant::now() + RECEIPT_TIMEOUT;\n        loop {\n            if let Some(receipt) = self\n                .provider\n                .get_transaction_receipt(tx_hash)\n                .await\n                .context(\"eth_getTransactionReceipt failed\")?\n            {\n                if !receipt.status() {\n                    anyhow::bail!(\"transaction {tx_hash} reverted (receipt status 0)\");\n                }\n                return Ok(receipt);\n            }\n            if Instant::now() >= deadline {\n                anyhow::bail!(\"transaction {tx_hash} not confirmed within {RECEIPT_TIMEOUT:?}\",);\n            }\n            tokio::time::sleep(self.receipt_poll_interval).await;\n        }\n    }\n\n    /// Returns the FungibleBridge contract address.\n    pub fn bridge_addr(&self) -> Address {\n        self.bridge_addr\n    }\n\n    /// Returns the EVM chain's latest block number.\n    pub async fn get_block_number(&self) -> Result<u64> {\n        Ok(self.provider.get_block_number().await?)\n    }\n\n    /// Returns the relayer's ETH balance in wei.\n    pub async fn get_relayer_balance(&self) -> Result<U256> {\n        Ok(self.provider.get_balance(self.relayer_addr).await?)","sourceCodeStart":71,"sourceCodeEnd":107,"githubUrl":"https://github.com/linera-io/linera-protocol/blob/6c226ddcb332ef55118dc8d0aafbd093d5420899/linera-bridge/src/relay/evm.rs#L71-L107","documentation":"await_receipt polled eth_getTransactionReceipt for a full RECEIPT_TIMEOUT (180s) and never saw the transaction mined. The tx may still be pending in the mempool (gas price too low for current congestion), dropped by the RPC provider, or the RPC endpoint is unhealthy. The timeout is deliberately generous so a slow-but-valid tx is not re-sent; the monitor's retry path re-sends after this error.","triggerScenarios":"Sending register_block/process_burns/add_committee txs with a gas price below what the current base fee demands during congestion; using a rate-limited or flaky public RPC that stops returning receipts; a chain reorg dropping the tx; the tx being silently dropped from the mempool.","commonSituations":"Public RPC endpoints (e.g. public Base Sepolia) throttling the relayer; network congestion spikes making the configured gas price uncompetitive; relay restarted mid-wait so the pending tx hash is lost and only resurfaces via this timeout on the next attempt.","solutions":["Let the monitor's retry path re-send (that is the designed behavior) — but first confirm the old tx is not still pending to avoid duplicates.","Raise the gas price / priority fee the relayer attaches to settlement txs so they confirm within 180s.","Switch to a reliable RPC provider (paid endpoint or own node) if receipts are intermittently unavailable.","If congestion is chronic, increase RECEIPT_TIMEOUT and/or shorten receipt_poll_interval in configuration."],"exampleFix":"// before\nif Instant::now() >= deadline {\n    anyhow::bail!(\"transaction {tx_hash} not confirmed within {RECEIPT_TIMEOUT:?}\");\n}\n\n// after (caller): treat as retryable, check pending state before re-sending\nmatch evm_client.await_receipt(tx_hash).await {\n    Ok(receipt) => { /* ... */ }\n    Err(e) if e.to_string().contains(\"not confirmed within\") => {\n        tracing::warn!(%tx_hash, %e, \"confirmation timeout; monitor retry will re-send\");\n        // return the error so the retry loop owns re-submission\n        return Err(e);\n    }\n    Err(e) => return Err(e),\n}","handlingStrategy":"retry","validationCode":"// Before sending, sanity-check the provider and gas market:\nlet block = provider.get_block_number().await?; // provider alive\n// ensure max_fee_per_gas >= current base fee * 1.2 so the tx is mineable within 180s.","typeGuard":null,"tryCatchPattern":"match evm_client.await_receipt(tx_hash).await {\n    Ok(r) => r,\n    Err(e) if e.to_string().contains(\"not confirmed within\") => {\n        // Retryable: confirm the old tx is not pending (eth_getTransactionByHash),\n        // then re-send with a higher fee via the monitor's retry path.\n        tracing::warn!(%tx_hash, \"{e}\");\n        return Err(e); // monitor retry loop re-sends\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Attach competitive EIP-1559 fees (base fee + margin) so settlement confirms well under 180s.","Use a dependable RPC endpoint; alert when receipt polling errors repeatedly.","Before re-sending after a timeout, check whether the original tx is still pending to avoid double-settling."],"tags":["linera-bridge","evm","transaction-timeout","gas","relay","rust","web3"],"backgroundTag":"transaction-confirmation-timeout","analyzedSha":"6c226ddcb332ef55118dc8d0aafbd093d5420899","analyzedAt":"2026-08-22T22:49:09.787Z","schemaVersion":2},"datasetVersion":"2026-08-23T01:17:44.959Z"}