stamparm/maltrail · warning
lookahead should match
Error message
lookahead should match
What it means
In the fancy-regex engine test `fancy_handles_lookahead`, `re.captures(...)` returns `Result<Option<Captures>>` and the test `.unwrap()`s the `Result` then `.expect("lookahead should match")`s the `Option`. The panic asserts the pattern `(?P<high>malware(?! (distribution|site)))|(?P<low>reputation)` matches 'known malware c2' via the `high` group; failure means the engine no longer handles named-group alternation with negative lookahead.
Solutions
- First test the pattern with a standalone fancy-regex instance (`Regex::new` + `captures`) to isolate whether the bug is in the engine or in `build_fancy`'s pattern rewriting.
- Print the rewritten pattern from `build_fancy` and verify the named groups and `(?! ...)` lookahead survive translation intact.
- Check the engine version's lookahead support; if the backend changed, fix lowering of negative lookahead so a non-matching suffix does not suppress the whole alternation branch.
- Split the test: assert `is_match` before asserting group captures, so match-failure and group-extraction regressions are distinguishable.
Example fix
// before
let hit = re.captures("known malware c2").unwrap().expect("lookahead should match");
// after (diagnose which stage failed)
let hit = re.captures("known malware c2")
.expect("captures returned Err")
.unwrap_or_else(|| panic!("no match for 'known malware c2' with pattern {}", re.as_str())); Defensive patterns
Strategy: validation
Validate before calling
// verify the pattern compiles and matches before asserting groups
assert!(re.is_match("known malware c2"), "pattern lost lookahead semantics"); Try / catch
let hit = re.captures("known malware c2")
.expect("captures Err")
.unwrap_or_else(|| panic!("no match; pattern={}", re.as_str())); Prevention
- Split is_match vs captures assertions to localize regressions
- Print the rewritten pattern when changing build_fancy
- Test negative-lookahead suppression cases next to positive cases
- Pin/upgrade the regex engine deliberately and rerun semantics tests
When it happens
Trigger: `build_fancy(...).captures("known malware c2")` returns `Ok(None)` (no match) or `Err` (regex compile/exec error), panicking at the expect. Triggered by regressions in the fancy regex backend: broken negative-lookahead semantics (over-suppressing), named-capture alternation bugs, or `build_fancy` compiling the pattern incorrectly.
Common situations: Upgrading or swapping the regex engine backing `build_fancy`; changes to the lookahead lowering/compilation; pattern-rewrite logic that mangles `(?P<name>...)` groups inside alternations; test refactor altering the sample text.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- invalid configuration value for 'REMOTE_SEVERITY_REGEX
- question
- well-formed hello parses
- SUSPICIOUS_HTTP_REQUEST_REGEXES must carry a 'code…
- forwarded-for regex
AI-assisted analysis of stamparm/maltrail@77cfb06d76 (2026-09-13).
Data as JSON: /api/errors/d41e6382e91f0042.
Report an issue: GitHub.
Appendix: source
Thrown at sensor/src/pyre.rs:364
// a leading flag group is fine, and scoped flags are fine anywhere
assert!(!has_late_global_flags(r"(?i)abc"));
assert!(!has_late_global_flags(r"a(?i:b)c"));
assert!(!has_late_global_flags(r"a(?P<n>b)c"));
assert!(!has_late_global_flags(r"a(?!b)c"));
assert!(!has_late_global_flags(r"a\(?i\)b"));
}
#[test]
fn punctuation_escapes_the_crate_dislikes_still_compile() {
// from data/ua.txt: Python treats \> as a literal '>'
let re = build(r"<script src=[^\>]*>").unwrap();
assert!(re.is_match("<script src=x>"));
}
#[test]
fn fancy_handles_lookahead() {
let re = build_fancy(r"(?P<high>malware(?! (distribution|site)))|(?P<low>reputation)").unwrap();
let hit = re.captures("known malware c2").unwrap().expect("lookahead should match");
assert!(hit.name("high").is_some());
// the negative lookahead suppresses the match entirely here
assert!(matches!(re.captures("malware distribution"), Ok(None)));
assert!(re.captures("reputation x").unwrap().unwrap().name("low").is_some());
}
}
View on GitHub (pinned to 77cfb06d76)