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
- Fix the regex literal syntax — validate it with a quick `regex::Regex::new(r"...")` in a scratch test or at regex101 (Rust flavor).
- Compile the regex once via a `static LazyLock<Regex>` instead of per-call `Regex::new(...).expect(...)`, per the repo's own regex rule.
- 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
- Compile regexes once in LazyLock statics, never inside the filter function
- Validate regex literals with a dedicated unit test so CI fails on typos
- Never panic in production filters — follow the repo fallback pattern (raw output)
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
- invalid ctest result prefix regex
- invalid ctest result terminator regex
- invalid ctest start regex
- invalid ctest summary regex
- invalid ctest time regex
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)