rtk-ai/rtk · error
invalid regex patterns
Error message
invalid regex patterns
What it means
This panic fires inside the LazyLock initializer of REGEX_SET (src/discover/registry.rs:54-56) when regex::RegexSet::new fails on any single pattern in the RULES table imported from src/discover/rules.rs. RegexSet is all-or-nothing: one bad pattern rejects the whole set, and `.expect` converts the Err into a process abort. Because compilation is deferred behind LazyLock, the panic happens at the FIRST dereference of REGEX_SET — i.e. the first `classify_command` call (first hooked command or `rtk discover`) — far from the rule edit that caused it, and it recurs on every subsequent classification attempt.
Source
Thrown at src/discover/registry.rs:55
},
"Cargo" => match subcmd {
"test" => 500,
_ => 150,
},
"Tests" => 800,
"Files" => 100,
"Build" => 300,
"Infra" => 120,
"Network" => 150,
"GitHub" => 200,
"GitLab" => 200,
"PackageManager" => 150,
_ => 150,
}
}
static REGEX_SET: LazyLock<RegexSet> = LazyLock::new(|| {
RegexSet::new(RULES.iter().map(|r| r.pattern)).expect("invalid regex patterns")
});
static COMPILED: LazyLock<Vec<Regex>> = LazyLock::new(|| {
RULES
.iter()
.map(|r| Regex::new(r.pattern).expect("invalid regex"))
.collect()
});
static ENV_PREFIX: LazyLock<Regex> = LazyLock::new(|| {
let double_quoted = r#""(?:[^"\\]|\\.)*""#;
let single_quoted = r#"'(?:[^'\\]|\\.)*'"#;
let unquoted = r#"[^\s]*"#;
let env_value = format!("(?:{}|{}|{})", double_quoted, single_quoted, unquoted);
let env_assign = format!(r#"[A-Z_][A-Z0-9_]*={}"#, env_value);
Regex::new(&format!(r#"^(?:sudo\s+|env\s+|{}\s+)+"#, env_assign)).unwrap()
});
// Git global options that appear before the subcommand: -C <path>, -c <key=val>,
// --git-dir <dir>, --work-tree <dir>, and flag-only options (#163)
static GIT_GLOBAL_OPT: LazyLock<Regex> = LazyLock::new(|| {View on GitHub (pinned to d977e1c316)
Solutions
- Run `cargo test` — or a quick scratch check that iterates RULES calling Regex::new on each pattern — to identify the offending pattern and its exact syntax error.
- Fix the pattern in src/discover/rules.rs: remove lookarounds/backreferences (restructure as alternation or anchored prefixes), escape stray metacharacters, balance groups/classes.
- Add a regression test: `#[test] fn all_rule_patterns_compile() { for r in RULES { Regex::new(r.pattern).unwrap_or_else(|e| panic!("{:?}: {}", r.pattern, e)); } }` so CI catches bad rules before runtime.
- Optionally make the initializer name the culprit instead of a generic message (see exampleFix) to shorten future diagnosis.
Example fix
// before (src/discover/registry.rs:54)
static REGEX_SET: LazyLock<RegexSet> = LazyLock::new(|| {
RegexSet::new(RULES.iter().map(|r| r.pattern)).expect("invalid regex patterns")
});
// after: report WHICH pattern failed, and guard with a test
static REGEX_SET: LazyLock<RegexSet> = LazyLock::new(|| {
for r in RULES.iter() {
if let Err(e) = Regex::new(r.pattern) {
panic!("invalid rule regex {:?}: {}", r.pattern, e);
}
}
RegexSet::new(RULES.iter().map(|r| r.pattern)).expect("invalid regex patterns")
});
#[test]
fn all_rule_patterns_compile() {
for r in RULES {
Regex::new(r.pattern).unwrap_or_else(|e| panic!("bad pattern {:?}: {}", r.pattern, e));
}
} Defensive patterns
Strategy: validation
Validate before calling
// In src/discover/registry.rs (or a rules test module) — run in CI:
#[test]
fn all_rule_patterns_compile() {
for r in super::super::rules::RULES {
regex::Regex::new(r.pattern)
.unwrap_or_else(|e| panic!("bad rule pattern {:?}: {}", r.pattern, e));
}
} Prevention
- Before merging any RULES change in src/discover/rules.rs, run the all-rule-patterns compile test — patterns are runtime &str data, so rustc will not catch a bad regex.
- Do not paste patterns from PCRE, sed, or GNU grep: the rust regex crate rejects lookaheads (?=...), lookbehinds (?<=...), and backreferences \1 — rewrite them as alternations or anchored prefixes.
- Smoke-test `rtk discover` (or any single hooked command) after rule edits, since LazyLock defers the panic to first classification and hides it from the edit that caused it.
- Prefer making the initializer panic message include the offending pattern (see exampleFix) so a future failure is diagnosed in seconds, not by bisecting the rules table.
When it happens
Trigger: Editing RULES in src/discover/rules.rs so any `pattern` field is rejected by the regex crate: unbalanced parens/brackets, invalid escapes (\m), bad character classes, or — most commonly — patterns pasted from PCRE/sed/GNU grep using lookaheads (?=...), lookbehinds (?<=...), or backreferences \1, none of which the rust regex crate supports. The crate compiles fine (patterns are &str data); the panic appears the first time rtk classifies any command.
Common situations: A contributor adds a rewrite rule for a new tool and copy-pastes a PCRE-style pattern with lookaround; `cargo build` and most tests pass; then every shell invocation intercepted by the rtk hook (or any `rtk discover` scan) panics, making it look like a systemic rtk breakage rather than a one-line rules regression.
Related errors
- invalid regex
- invalid nextest summary regex
- invalid nextest starting regex
- stdout streaming thread panicked
- stderr streaming thread panicked
AI-assisted analysis of rtk-ai/rtk@d977e1c316 (2026-08-16).
Data as JSON: /api/errors/0e90b9ca7661dae8.
Report an issue: GitHub.