rtk-ai/rtk · error

invalid nextest summary regex

Error message

invalid nextest summary regex

What it means

Panic from `.expect("invalid nextest summary regex")` after `regex::Regex::new` in `filter_cargo_nextest` (src/cmds/rust/cargo_cmd.rs). Rust's regex crate returns `Err` for syntactically invalid patterns, and the code turns that into a panic. Because the pattern is a hardcoded literal, this fires only at the first call to `filter_cargo_nextest` (i.e. in its unit tests), crashing the filter instead of degrading gracefully.

Solutions

  1. Fix the regex literal syntax — validate it with a quick `regex::Regex::new(r"...")` in a scratch test or at regex101 (Rust flavor).
  2. Compile the regex once via a `static LazyLock<Regex>` instead of per-call `Regex::new(...).expect(...)`, per the repo's own regex rule.
  3. If the pattern must stay dynamic, return `anyhow::Result` and fall back to raw output instead of panicking, following the repo's fallback pattern.

Example fix

// before
let summary_re = regex::Regex::new(r"Summary \[\s*([\d.]+)s\]...")
    .expect("invalid nextest summary regex");
// after
static SUMMARY_RE: LazyLock<Regex> = LazyLock::new(|| {
    Regex::new(r"Summary \[\s*([\d.]+)s\]...").expect("invalid nextest summary regex")
});
Defensive patterns

Strategy: try-catch

Validate before calling

// compile-time-ish check: unit test that builds all filter regexes
#[test]
fn nextest_regexes_are_valid() {
    regex::Regex::new(SUMMARY_PAT).unwrap();
}

Try / catch

match regex::Regex::new(SUMMARY_PAT) {
    Ok(re) => re,
    Err(e) => {
        eprintln!("nextest summary regex invalid: {e}");
        return output.to_string(); // fallback: raw output
    }
}

Prevention

When it happens

Trigger: Editing the hardcoded `Summary [...]` regex literal and introducing a syntax error (unbalanced `(?:...)` group, bad escape, invalid repetition); the panic then fires on every `filter_cargo_nextest` call, first observed when running the six filter_cargo_nextest unit tests.

Common situations: Hand-editing the nextest summary pattern to match a new cargo-nextest output format and mistyping an escape like `\s` or leaving a group unclosed; regex crate version upgrades rarely matter here since the pattern is static.

Related errors


AI-assisted analysis of rtk-ai/rtk@0924356b4c (2026-09-19). Data as JSON: /api/errors/0a85e08e6e7684fe. Report an issue: GitHub.

Appendix: source

Thrown at src/cmds/rust/cargo_cmd.rs:636

fn flush_failure_block(header: &mut String, body: &mut Vec<String>, failures: &mut Vec<String>) {
    if header.is_empty() {
        return;
    }
    let mut block = header.clone();
    if !body.is_empty() {
        block.push('\n');
        block.push_str(&body.join("\n"));
    }
    failures.push(block);
    header.clear();
    body.clear();
}

/// Filter cargo nextest output - show failures + compact summary
fn filter_cargo_nextest(output: &str) -> String {
    let summary_re = regex::Regex::new(
        r"Summary \[\s*([\d.]+)s\]\s+(\d+) tests? run:\s+(\d+) passed(?:,\s+(\d+) failed)?(?:,\s+(\d+) skipped)?"
    ).expect("invalid nextest summary regex");

    let starting_re = regex::Regex::new(r"Starting \d+ tests? across (\d+) binar(?:y|ies)")
        .expect("invalid nextest starting regex");

    let mut failures: Vec<String> = Vec::new();
    let mut in_failure_block = false;
    let mut past_summary = false;
    let mut current_failure_header = String::new();
    let mut current_failure_body = Vec::new();
    let mut summary_line = String::new();
    let mut binaries: u32 = 0;
    let mut has_cancel_line = false;

    for line in output.lines() {
        let trimmed = line.trim();

        // Strip compilation noise
        if trimmed.starts_with("Compiling")

View on GitHub (pinned to 0924356b4c)