unionlabs/union · error · anyhow::Error

wait_for_packet_ack failed: {:?}

Error message

wait_for_packet_ack failed: {:?}

What it means

In `send_and_recv_withdraw_rewards`, after the withdraw-rewards event is seen the code waits for the source chain to observe the packet acknowledgement via `wait_for_packet_ack(packet_hash, timeout)`; failure means no ack event for that hash within the timeout.

Source

Thrown at tools/union-test/src/lib.rs:843

        };
        println!(
            "Packet sent from {} to {} with hash: {}",
            source_chain.chain_id(),
            destination_chain.chain_id(),
            packet_hash
        );

        let withdraw_rewards = match destination_chain
            .wait_for_withdraw_rewards(validator, timeout)
            .await
        {
            Ok(evt) => Ok(evt),
            Err(e) => anyhow::bail!("wait_for_packet_recv failed: {:?}", e),
        };

        match source_chain.wait_for_packet_ack(packet_hash, timeout).await {
            Ok(evt) => evt,
            Err(e) => anyhow::bail!("wait_for_packet_ack failed: {:?}", e),
        };

        withdraw_rewards
    }

    pub async fn send_and_recv_refund<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
        {

View on GitHub (pinned to 031785bb6d)

Solutions

  1. Increase `timeout`
  2. Verify relayer health on the destination-to-source direction
  3. Check destination tx logs to confirm an acknowledgement was written rather than a timeout packet

Example fix

// before
... .await?; // uses Duration::from_secs(120) for the ack wait 
// after: size the ack timeout to relayer round-trip latency
match source_chain.wait_for_packet_ack(packet_hash, Duration::from_secs(900)).await {
    Ok(evt) => evt,
    Err(e) => anyhow::bail!("wait_for_packet_ack failed: {:?}", e),
}
Defensive patterns

Strategy: retry

Try / catch

let mut n = 0;
loop {
    match source_chain.wait_for_packet_ack(packet_hash, timeout).await {
        Ok(evt) => break Ok(evt),
        Err(e) if n < 2 => { n += 1; source_chain.wait_for_packet_ack(packet_hash, timeout * 2).await.ok().map(|e| break Ok(e)); continue; }
        Err(e) => break Err(anyhow::anyhow!("wait_for_packet_ack failed: {:?}", e)),
    }
}

Prevention

When it happens

Trigger: The relayer didn't relay the ack back to the source chain in time; the source chain stalled; or the packet timed out on the destination (timeout packet instead of an ack) so no acknowledgement is ever written.

Common situations: Slow relayers on public testnets; timeouts configured below round-trip relay latency; destination-side errors producing timeout packets rather than acks.

Related errors


AI-assisted analysis of unionlabs/union@031785bb6d (2026-08-16). Data as JSON: /api/errors/6522a6add00196bf. Report an issue: GitHub.