unionlabs/union · error · anyhow::Error

Expected revert 0x{:08x}, but transaction succeeded

Error message

Expected revert 0x{:08x}, but transaction succeeded

What it means

send_and_expect_revert deliberately sends a tx that should fail on-chain and then matches the revert selector against expected_revert_code. This error means the opposite happened: send_ibc_transaction returned Ok, i.e. the transaction succeeded (or at least was accepted) when the test asserted a specific revert. Either the contract logic changed so the path no longer reverts, or the test set the wrong expectation.

Source

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

        };
        println!("Update client event received: {:?}", update_client_result);

        Ok(update_client_result.unwrap().height)
    }

    pub async fn send_and_expect_revert<Src: ChainEndpoint, Dst: ChainEndpoint>(
        &self,
        source_chain: &Src,
        contract: Src::Contract,
        msg: Src::Msg,
        expected_revert_code: u32,
        signer: &Src::ProviderType,
    ) -> anyhow::Result<()> {
        match source_chain
            .send_ibc_transaction(contract.clone(), msg.clone(), signer)
            .await
        {
            Ok((_, _)) => anyhow::bail!(
                "Expected revert 0x{:08x}, but transaction succeeded",
                expected_revert_code
            ),

            Err(e) => {
                let err_str = format!("{:#}", e);

                let re = Regex::new(r"(0x[0-9A-Fa-f]+)").unwrap();
                let caps = re.captures(&err_str).ok_or_else(|| {
                    anyhow!(
                        "Transaction reverted but no rawValue hex found: {}",
                        err_str
                    )
                })?;

                let hex_full = caps
                    .get(1)
                    .map(|m| m.as_str())

View on GitHub (pinned to 031785bb6d)

Solutions

  1. Decide whether the tx should now succeed: if the contract behavior intentionally changed, switch to the success-path helper (send_and_recv_ack) instead of expecting a revert.
  2. If a revert is still expected, reproduce the exact precondition that triggers it (drain the balance, remove the channel, etc.) before the call.
  3. Recompute expected_revert_code from the current contract's error selector table (keccak of the error signature's first 4 bytes).
  4. Check you are testing the right contract address / build artifacts.

Example fix

// before: contract was changed to succeed on this path
tw.send_and_expect_revert(&src, contract, msg, EXPECTED_REVERT, &signer).await?;

// after: assert the now-successful path
tw.send_and_recv_ack(&src, contract, msg, &dst, timeout, &signer).await?;
Defensive patterns

Strategy: validation

Validate before calling

// confirm the precondition that makes the branch revert before asserting it
let bal = contract.balance_of(signer.address()).await?;
anyhow::ensure!(bal < amount, "balance covers amount — this path will succeed, not revert");

Try / catch

match tw.send_and_expect_revert(&src, contract, msg, code, &signer).await {
    Err(e) if e.to_string().contains("transaction succeeded") => { /* precondition lost: re-establish it and retry once */ }
    other => other?,
}

Prevention

When it happens

Trigger: Calling send_and_expect_revert with expected_revert_code for a branch the contract no longer reverts on (guard conditions changed, fixture state different), or passing a msg that hits a success path — e.g. transfer that now succeeds because balances were topped up by a previous test.

Common situations: Contract upgrade removes/changes a revert; shared test-chain state (funds arrived, channel created) invalidating the assumption of failure; wrong msg constructed so it takes the happy path; expected selector computed from a stale ABI.

Related errors


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