{"record":{"id":"2746f1ab75441a92","repo":"unionlabs/union","slug":"regex-matched-but-capture-group-missing","errorCode":null,"errorMessage":"regex matched but capture group missing","messagePattern":"regex matched but capture group missing","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"warning","filePath":"tools/union-test/src/lib.rs","lineNumber":1046,"sourceCode":"                \"Expected revert 0x{:08x}, but transaction succeeded\",\n                expected_revert_code\n            ),\n\n            Err(e) => {\n                let err_str = format!(\"{:#}\", e);\n\n                let re = Regex::new(r\"(0x[0-9A-Fa-f]+)\").unwrap();\n                let caps = re.captures(&err_str).ok_or_else(|| {\n                    anyhow!(\n                        \"Transaction reverted but no rawValue hex found: {}\",\n                        err_str\n                    )\n                })?;\n\n                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,","sourceCodeStart":1028,"sourceCodeEnd":1064,"githubUrl":"https://github.com/unionlabs/union/blob/031785bb6dc6b957c624e62bc64c184409c97d7b/tools/union-test/src/lib.rs#L1028-L1064","documentation":"A defensive, effectively unreachable branch in send_and_expect_revert: Regex::captures succeeded (so group 1 exists by construction of the regex (0x[0-9A-Fa-f]+), which has exactly one capturing group), yet caps.get(1) returned None and the code bails with 'regex matched but capture group missing'. In practice you should never see this; hitting it indicates a regex crate version/behavior anomaly or corrupted logic.","triggerScenarios":"Only producible if the regex pattern were edited to include an optional capture group that did not participate in the match, or an incompatible regex crate regression — the current pattern always populates group 1 on a match.","commonSituations":"Someone modifies the regex to (0x...)?-style optional groups; pathological regex crate downgrade/upgrade. Otherwise this is dead defensive code.","solutions":["If encountered, check whether the regex literal near lib.rs:1042 was modified to use optional/non-participating groups; restore a single mandatory group.","Simplify by using re.captures(...).and_then(|c| c.get(1)) and treating both as the 111 case, removing the impossible branch.","Verify regex crate version in Cargo.lock is the one the code was written for."],"exampleFix":null,"handlingStrategy":"type-guard","validationCode":null,"typeGuard":"fn first_capture(caps: &regex::Captures<'_>) -> Option<&str> {\n    caps.get(1).map(|m| m.as_str()) // single mandatory group always participates\n}","tryCatchPattern":null,"preventionTips":["Keep exactly one mandatory capture group in the selector regex.","Cover this parsing with a unit test feeding representative provider error strings."],"tags":["rust","regex","defensive-code","unreachable"],"backgroundTag":null,"analyzedSha":"031785bb6dc6b957c624e62bc64c184409c97d7b","analyzedAt":"2026-08-16T06:24:09.996Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}