{"record":{"id":"fd5cd39b739bba2d","repo":"unionlabs/union","slug":"failed-to-send-transaction","errorCode":null,"errorMessage":"failed to send transaction","messagePattern":"failed to send transaction","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"tools/union-test/src/cosmos.rs","lineNumber":779,"sourceCode":"\n    /// Helper to detect the ABCI “account sequence mismatch” error.\n    fn is_sequence_mismatch(&self, err: &BroadcastTxCommitError) -> bool {\n        match err {\n            BroadcastTxCommitError::Query(grpc_err) => {\n                grpc_err.log.contains(\"account sequence mismatch\")\n            }\n            _ => false,\n        }\n    }\n\n    pub async fn send_ibc_transaction(\n        &self,\n        contract: Addr,\n        msg: (Vec<u8>, Vec<Coin>),\n        signer: &LocalSigner,\n    ) -> anyhow::Result<(H256, u64)> {\n        let result = self.send_cosmwasm_transaction(contract, msg, signer).await;\n        let tx_result = result.ok_or_else(|| anyhow!(\"failed to send transaction\"))??;\n        let height = tx_result\n            .height\n            .ok_or_else(|| anyhow!(\"transaction height not found\"))?;\n\n        let send_event = tx_result\n            .tx_result\n            .events\n            .into_iter()\n            .find_map(|e| {\n                if e.ty == \"wasm-packet_send\" {\n                    CosmosSdkEvent::<ModuleEvent>::new(e).ok().map(|e| e.event)\n                } else {\n                    None\n                }\n            })\n            .ok_or_else(|| anyhow!(\"wasm-packet_send event not found\"))?;\n\n        Ok(match send_event {","sourceCodeStart":761,"sourceCodeEnd":797,"githubUrl":"https://github.com/unionlabs/union/blob/031785bb6dc6b957c624e62bc64c184409c97d7b/tools/union-test/src/cosmos.rs#L761-L797","documentation":"`send_ibc_transaction` calls `send_cosmwasm_transaction_with_retry`, which retries up to 5 attempts but only on account-sequence-mismatch errors; it returns None only when every attempt failed with a sequence mismatch. This `ok_or_else` turns that None into 'failed to send transaction', so the error specifically means the signer's sequence stayed out of sync with the node for the full retry window (~25s).","triggerScenarios":"Concurrent transactions broadcast from the same LocalSigner while earlier ones are still uncommitted; an RPC node lagging on account sequence after restart; a previously broadcast tx not yet committed when each retry re-derives the sequence.","commonSituations":"Tests firing multiple sends in parallel with one signer; slow single-validator devnets; stale sequence state after node crash/restart; the same test key reused by another process.","solutions":["Serialize sends from the same signer: await each send before broadcasting the next","Wait a few seconds for in-flight txs to commit, then retry the whole send","Reconnect to / restart a healthy RPC node if sequences are stuck","Give each concurrent test flow its own funded account"],"exampleFix":"// before: two sends from one signer in parallel\ntokio::join!(\n    client.send_ibc_transaction(c.clone(), m1, &signer),\n    client.send_ibc_transaction(c, m2, &signer),\n); \n// after: serialize sends from the same account\nclient.send_ibc_transaction(c.clone(), m1, &signer).await?;\nclient.send_ibc_transaction(c, m2, &signer).await?;","handlingStrategy":"retry","validationCode":"// pre-flight: no other in-flight txs for this signer\nlet status = client.rpc.status().await?; // and account_info query for the sequence\n// ensure prior broadcasts have committed before sending the next one","typeGuard":null,"tryCatchPattern":"let mut attempts = 0;\nloop {\n    match client.send_ibc_transaction(contract.clone(), msg.clone(), &signer).await {\n        Ok(r) => break Ok(r),\n        Err(e) if e.to_string().contains(\"failed to send transaction\") && attempts < 3 => {\n            attempts += 1;\n            tokio::time::sleep(Duration::from_secs(15)).await; // let in-flight txs commit\n            continue;\n        }\n        Err(e) => break Err(e),\n    }\n}","preventionTips":["Serialize all sends from one signer; never broadcast concurrently with the same account","Give each parallel test flow its own funded account","After node restarts, wait for the sequence to settle before resuming"],"tags":["rust","cosmos","sequence-mismatch","retry","transaction"],"backgroundTag":null,"analyzedSha":"031785bb6dc6b957c624e62bc64c184409c97d7b","analyzedAt":"2026-08-16T06:24:09.996Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}