rtk-ai/rtk · error

invalid nextest summary regex

Error message

invalid nextest summary regex

What it means

This panic fires when regex::Regex::new cannot compile the hardcoded nextest summary pattern (r"Summary \[\s*([\d.]+)s\]...") inside filter_cargo_nextest (src/cmds/rust/cargo_cmd.rs:623-625). Because the pattern is a string literal, the regex crate parses it at runtime on every call; the only way to get Err is a developer editing the literal into an invalid regex (unbalanced `(` or `[`, bad escape, etc.) — no cargo nextest output can cause it. It also violates the repo rule (.claude/rules/rust-patterns.md) that all regex live in LazyLock statics, so the pattern is needlessly recompiled per invocation.

Source

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

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 d977e1c316)

Solutions

  1. Fix the literal: paste the pattern into a scratch `fn main` with Regex::new(...).unwrap() or an online rust-regex checker to get the exact syntax error, then correct it in src/cmds/rust/cargo_cmd.rs:624.
  2. Hoist it into a module-level `static SUMMARY_RE: LazyLock<Regex>` with the same expect, matching the repo's LazyLock convention and removing the per-call recompile.
  3. Add a unit test that compiles the regex and matches a real summary line (e.g. "Summary [ 12.34s] 42 tests run: 40 passed, 2 failed") so an invalid pattern fails `cargo test` before shipping.

Example fix

// before (src/cmds/rust/cargo_cmd.rs:622)
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");

// after: compile once, fail fast and legibly
static SUMMARY_RE: std::sync::LazyLock<regex::Regex> = std::sync::LazyLock::new(|| {
    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")
});

#[test]
fn nextest_summary_regex_matches() {
    assert!(SUMMARY_RE.is_match("Summary [ 2.345s] 42 tests run: 41 passed, 1 failed"));
}
Defensive patterns

Strategy: validation

Validate before calling

// Add next to filter_cargo_nextest — fails `cargo test` before any runtime panic:
#[test]
fn nextest_regexes_compile_and_match() {
    let summary = 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");
    assert!(summary.is_match("Summary [ 2.345s] 42 tests run: 41 passed, 1 failed"));
}

Prevention

When it happens

Trigger: Any run that reaches filter_cargo_nextest — i.e. `rtk cargo nextest` or a hooked `cargo nextest` invocation — after the literal was edited into an invalid regex. Regex::new runs unconditionally at the top of the function, so the process panics before any output parsing, even when the run's output contains no Summary line at all. The crate still compiles because the pattern is an ordinary &str; nothing checks it at build time.

Common situations: A contributor adapts the pattern for a new nextest release's summary format (e.g. adding another optional group like (?:,\s+(\d+) skipped)?) and mistypes a bracket or parenthesis; CI is green unless a nextest fixture test executes; the first real `rtk cargo nextest` run then aborts with this message.

Related errors


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