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

  1. 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.
  2. Fix the pattern in src/discover/rules.rs: remove lookarounds/backreferences (restructure as alternation or anchored prefixes), escape stray metacharacters, balance groups/classes.
  3. 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.
  4. 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

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


AI-assisted analysis of rtk-ai/rtk@d977e1c316 (2026-08-16). Data as JSON: /api/errors/0e90b9ca7661dae8. Report an issue: GitHub.