{"record":{"id":"0e90b9ca7661dae8","repo":"rtk-ai/rtk","slug":"invalid-regex-patterns","errorCode":null,"errorMessage":"invalid regex patterns","messagePattern":"invalid regex patterns","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/discover/registry.rs","lineNumber":55,"sourceCode":"        },\n        \"Cargo\" => match subcmd {\n            \"test\" => 500,\n            _ => 150,\n        },\n        \"Tests\" => 800,\n        \"Files\" => 100,\n        \"Build\" => 300,\n        \"Infra\" => 120,\n        \"Network\" => 150,\n        \"GitHub\" => 200,\n        \"GitLab\" => 200,\n        \"PackageManager\" => 150,\n        _ => 150,\n    }\n}\n\nstatic REGEX_SET: LazyLock<RegexSet> = LazyLock::new(|| {\n    RegexSet::new(RULES.iter().map(|r| r.pattern)).expect(\"invalid regex patterns\")\n});\nstatic COMPILED: LazyLock<Vec<Regex>> = LazyLock::new(|| {\n    RULES\n        .iter()\n        .map(|r| Regex::new(r.pattern).expect(\"invalid regex\"))\n        .collect()\n});\nstatic ENV_PREFIX: LazyLock<Regex> = LazyLock::new(|| {\n    let double_quoted = r#\"\"(?:[^\"\\\\]|\\\\.)*\"\"#;\n    let single_quoted = r#\"'(?:[^'\\\\]|\\\\.)*'\"#;\n    let unquoted = r#\"[^\\s]*\"#;\n    let env_value = format!(\"(?:{}|{}|{})\", double_quoted, single_quoted, unquoted);\n    let env_assign = format!(r#\"[A-Z_][A-Z0-9_]*={}\"#, env_value);\n    Regex::new(&format!(r#\"^(?:sudo\\s+|env\\s+|{}\\s+)+\"#, env_assign)).unwrap()\n});\n// Git global options that appear before the subcommand: -C <path>, -c <key=val>,\n// --git-dir <dir>, --work-tree <dir>, and flag-only options (#163)\nstatic GIT_GLOBAL_OPT: LazyLock<Regex> = LazyLock::new(|| {","sourceCodeStart":37,"sourceCodeEnd":73,"githubUrl":"https://github.com/rtk-ai/rtk/blob/d977e1c31621fe8704e6500ceeb9c7a0de2b6836/src/discover/registry.rs#L37-L73","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before (src/discover/registry.rs:54)\nstatic REGEX_SET: LazyLock<RegexSet> = LazyLock::new(|| {\n    RegexSet::new(RULES.iter().map(|r| r.pattern)).expect(\"invalid regex patterns\")\n});\n\n// after: report WHICH pattern failed, and guard with a test\nstatic REGEX_SET: LazyLock<RegexSet> = LazyLock::new(|| {\n    for r in RULES.iter() {\n        if let Err(e) = Regex::new(r.pattern) {\n            panic!(\"invalid rule regex {:?}: {}\", r.pattern, e);\n        }\n    }\n    RegexSet::new(RULES.iter().map(|r| r.pattern)).expect(\"invalid regex patterns\")\n});\n\n#[test]\nfn all_rule_patterns_compile() {\n    for r in RULES {\n        Regex::new(r.pattern).unwrap_or_else(|e| panic!(\"bad pattern {:?}: {}\", r.pattern, e));\n    }\n}","handlingStrategy":"validation","validationCode":"// In src/discover/registry.rs (or a rules test module) — run in CI:\n#[test]\nfn all_rule_patterns_compile() {\n    for r in super::super::rules::RULES {\n        regex::Regex::new(r.pattern)\n            .unwrap_or_else(|e| panic!(\"bad rule pattern {:?}: {}\", r.pattern, e));\n    }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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."],"tags":["regex","regexset","lazylock","panic","rules","discover","lookahead-unsupported"],"backgroundTag":null,"analyzedSha":"d977e1c31621fe8704e6500ceeb9c7a0de2b6836","analyzedAt":"2026-08-16T05:40:46.291Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}