{"record":{"id":"137ec2e883151173","repo":"unionlabs/union","slug":"expected-selector-0x-08x-got-0x-08x-raw","errorCode":null,"errorMessage":"Expected selector 0x{:08x}, got 0x{:08x} (raw: {})","messagePattern":"Expected selector 0x(.+?), got 0x(.+?) \\(raw: (.+?)\\)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"tools/union-test/src/lib.rs","lineNumber":1061,"sourceCode":"                let hex_full = caps\n                    .get(1)\n                    .map(|m| m.as_str())\n                    .ok_or_else(|| anyhow!(\"regex matched but capture group missing\"))?;\n\n                let hexdigits = hex_full.trim_start_matches(\"0x\");\n                if hexdigits.len() < 8 {\n                    anyhow::bail!(\"rawValue too short for a selector: {}\", hex_full);\n                }\n                let selector_hex = &hexdigits[..8]; // first 4 bytes\n\n                let actual = u32::from_str_radix(selector_hex, 16).with_context(|| {\n                    format!(\"parsing revert selector `{selector_hex}` from `{hex_full}`\")\n                })?;\n\n                if actual == expected_revert_code {\n                    Ok(())\n                } else {\n                    anyhow::bail!(\n                        \"Expected selector 0x{:08x}, got 0x{:08x} (raw: {})\",\n                        expected_revert_code,\n                        actual,\n                        hex_full\n                    )\n                }\n            }\n        }\n    }\n\n    pub async fn send_and_recv_withdraw<Src: ChainEndpoint, Dst: ChainEndpoint>(\n        &self,\n        source_chain: &Src,\n        contract: Src::Contract,\n        msg: Src::Msg,\n        destination_chain: &Dst,\n        timeout: Duration,\n        signer: Src::ProviderType,","sourceCodeStart":1043,"sourceCodeEnd":1079,"githubUrl":"https://github.com/unionlabs/union/blob/031785bb6dc6b957c624e62bc64c184409c97d7b/tools/union-test/src/lib.rs#L1043-L1079","documentation":"The final assertion failure of send_and_expect_revert: a selector-shaped hex was extracted and its first 4 bytes parsed as u32, but actual != expected_revert_code. The tx reverted with a different error than the test asserted. The message helpfully prints both selectors and the full raw hex so you can identify which contract error actually fired.","triggerScenarios":"The msg hits a different require/error branch than intended (e.g. INSUFFICIENT_FUNDS instead of UNAUTHORIZED because fixture state differs), expected_revert_code computed from the wrong error signature or a stale ABI, or big-endian/endianness or digit-order mistake when hand-writing the expected constant.","commonSituations":"Contract error enum reordered or renamed after an upgrade while the test constant stayed; shared chain state changed which guard trips first; expected selector written with padded wrong width (0x{:08x} formatting hides missing digits).","solutions":["Decode the actual selector in the message against the contract's error list (e.g. cast 4byte / selector tables) to learn which error fired, then fix the precondition so the intended branch trips.","Recompute expected_revert_code from the current ABI: keccak256(\"ErrorName(type1,type2)\")[0..4].","If the actual error reveals a test-fixture problem (missing funds, channel not set up), fix the setup, not the constant.","Prefer deriving the expected selector in code from the typed contract bindings rather than a hand-written u32."],"exampleFix":"// before — hand-written selector for the old ABI\nconst EXPECTED: u32 = 0x9e87c1a2;\n\n// after — derive it from current bindings\nuse contract::Error;\nlet expected = u32::from_be_bytes(Error::Unauthorized.selector());","handlingStrategy":"validation","validationCode":"// assert intent before the call: derive expected from live bindings\nlet expected = u32::from_be_bytes(MyError::InsufficientFunds.selector());\nassert_eq!(expected, EXPECTED_REVERT_CODE, \"fixture selector drifted from ABI\");","typeGuard":null,"tryCatchPattern":"match tw.send_and_expect_revert(&src, contract, msg, expected, &signer).await {\n    Err(e) if e.to_string().contains(\"Expected selector\") => { /* decode actual selector, fix precondition */ panic!(\"{e:#}\") }\n    other => other?,\n}","preventionTips":["Compute expected selectors from bindings each run instead of hard-coding.","Name the expected error in test comments and assert precondition state before the send."],"tags":["ibc","rust","revert","selector","assertion"],"backgroundTag":null,"analyzedSha":"031785bb6dc6b957c624e62bc64c184409c97d7b","analyzedAt":"2026-08-16T06:24:09.996Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}