unionlabs/union · error · anyhow::Error
send_ibc_transaction failed: {:?}
Error message
send_ibc_transaction failed: {:?} What it means
Top-level wrapper in union-test's e2e driver (`send_and_recv`): it calls `source_chain.send_ibc_transaction(...)` (EVM or Cosmos implementation depending on chain type) and re-wraps any failure with this message. The real cause is always the attached inner error printed with `{:?}`.
Source
Thrown at tools/union-test/src/lib.rs:698
pub async fn send_and_recv<Src: ChainEndpoint, Dst: ChainEndpoint>(
&self,
source_chain: &Src,
contract: Src::Contract,
msg: Src::Msg,
destination_chain: &Dst,
timeout: Duration,
signer: &Src::ProviderType,
) -> anyhow::Result<helpers::PacketRecv> {
let (packet_hash, _height) = match source_chain
.send_ibc_transaction(contract.clone(), msg.clone(), signer)
.await
{
Ok(hash) => {
println!("send_ibc_tx succeeded with hash: {:?}", hash);
hash
}
Err(e) => {
anyhow::bail!("send_ibc_transaction failed: {:?}", e);
}
};
println!(
"Packet sent from {} to {} with hash: {}",
source_chain.chain_id(),
destination_chain.chain_id(),
packet_hash
);
match destination_chain
.wait_for_packet_recv(packet_hash, timeout)
.await
{
Ok(evt) => Ok(evt),
Err(e) => anyhow::bail!("wait_for_packet_recv failed: {:?}", e),
}
}
pub async fn send_and_recv_and_ack<Src: ChainEndpoint, Dst: ChainEndpoint>(View on GitHub (pinned to 031785bb6d)
Solutions
- Read the inner error in the bail message and fix that specific cause (funds, sequence, RPC, msg encoding)
- If the inner cause is transient (sequence mismatch, RPC hiccup), wait briefly and re-run the test
- Verify the source chain signer and contract addresses in the fixture
Example fix
// before
suite.send_and_recv(src, contract, msg, dst, Duration::from_secs(120), &signer).await?;
// after: one retry for transient source-side send failures
let r = suite.send_and_recv(src, contract, msg.clone(), dst, Duration::from_secs(120), &signer).await;
let recv = match r {
Err(e) if is_transient_send_err(&e) => {
tokio::time::sleep(Duration::from_secs(10)).await;
suite.send_and_recv(src, contract, msg, dst, Duration::from_secs(120), &signer).await?
}
r => r?,
}; Defensive patterns
Strategy: try-catch
Validate before calling
// pre-flight: source signer can broadcast (cosmos: sequence settles; evm: funds present) let _ = source_chain.get_fee_payer_balance().await?; // fails fast on setup issues
Try / catch
match suite.send_and_recv(src, contract, msg.clone(), dst, timeout, &signer).await {
Ok(r) => Ok(r),
Err(e) if e.to_string().starts_with("send_ibc_transaction failed") && is_transient(&e) => {
tokio::time::sleep(Duration::from_secs(10)).await;
suite.send_and_recv(src, contract, msg, dst, timeout, &signer).await
}
Err(e) => Err(e),
} Prevention
- Keep source-chain signers funded and dedicated to one test flow
- Log the inner error chain (`{:#}`) so wrapper failures are diagnosable
- Validate contract/msg fixtures before running the e2e step
When it happens
Trigger: Any failure of the underlying send: cosmos sequence-mismatch exhaustion ('failed to send transaction'), missing wasm-packet_send event, missing tx height, or on EVM a keyring/RPC broadcast failure. In all cases the packet never left the source chain.
Common situations: Unfunded or mis-sequenced signer; msg encoding that doesn't produce a packet; RPC issues on the source chain — anything that makes the source-side send step of an e2e test fail.
Related errors
AI-assisted analysis of unionlabs/union@031785bb6d (2026-08-16).
Data as JSON: /api/errors/2e6310fc1fbcf832.
Report an issue: GitHub.