{"record":{"id":"0a85e08e6e7684fe","repo":"rtk-ai/rtk","slug":"invalid-nextest-summary-regex","errorCode":null,"errorMessage":"invalid nextest summary regex","messagePattern":"invalid nextest summary regex","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/cmds/rust/cargo_cmd.rs","lineNumber":625,"sourceCode":"fn flush_failure_block(header: &mut String, body: &mut Vec<String>, failures: &mut Vec<String>) {\n    if header.is_empty() {\n        return;\n    }\n    let mut block = header.clone();\n    if !body.is_empty() {\n        block.push('\\n');\n        block.push_str(&body.join(\"\\n\"));\n    }\n    failures.push(block);\n    header.clear();\n    body.clear();\n}\n\n/// Filter cargo nextest output - show failures + compact summary\nfn filter_cargo_nextest(output: &str) -> String {\n    let summary_re = regex::Regex::new(\n        r\"Summary \\[\\s*([\\d.]+)s\\]\\s+(\\d+) tests? run:\\s+(\\d+) passed(?:,\\s+(\\d+) failed)?(?:,\\s+(\\d+) skipped)?\"\n    ).expect(\"invalid nextest summary regex\");\n\n    let starting_re = regex::Regex::new(r\"Starting \\d+ tests? across (\\d+) binar(?:y|ies)\")\n        .expect(\"invalid nextest starting regex\");\n\n    let mut failures: Vec<String> = Vec::new();\n    let mut in_failure_block = false;\n    let mut past_summary = false;\n    let mut current_failure_header = String::new();\n    let mut current_failure_body = Vec::new();\n    let mut summary_line = String::new();\n    let mut binaries: u32 = 0;\n    let mut has_cancel_line = false;\n\n    for line in output.lines() {\n        let trimmed = line.trim();\n\n        // Strip compilation noise\n        if trimmed.starts_with(\"Compiling\")","sourceCodeStart":607,"sourceCodeEnd":643,"githubUrl":"https://github.com/rtk-ai/rtk/blob/d977e1c31621fe8704e6500ceeb9c7a0de2b6836/src/cmds/rust/cargo_cmd.rs#L607-L643","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["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.","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.","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."],"exampleFix":"// before (src/cmds/rust/cargo_cmd.rs:622)\nfn filter_cargo_nextest(output: &str) -> String {\n    let summary_re = regex::Regex::new(\n        r\"Summary \\[\\s*([\\d.]+)s\\]\\s+(\\d+) tests? run:\\s+(\\d+) passed(?:,\\s+(\\d+) failed)?(?:,\\s+(\\d+) skipped)?\"\n    ).expect(\"invalid nextest summary regex\");\n\n// after: compile once, fail fast and legibly\nstatic SUMMARY_RE: std::sync::LazyLock<regex::Regex> = std::sync::LazyLock::new(|| {\n    regex::Regex::new(\n        r\"Summary \\[\\s*([\\d.]+)s\\]\\s+(\\d+) tests? run:\\s+(\\d+) passed(?:,\\s+(\\d+) failed)?(?:,\\s+(\\d+) skipped)?\"\n    ).expect(\"invalid nextest summary regex\")\n});\n\n#[test]\nfn nextest_summary_regex_matches() {\n    assert!(SUMMARY_RE.is_match(\"Summary [ 2.345s] 42 tests run: 41 passed, 1 failed\"));\n}","handlingStrategy":"validation","validationCode":"// Add next to filter_cargo_nextest — fails `cargo test` before any runtime panic:\n#[test]\nfn nextest_regexes_compile_and_match() {\n    let summary = regex::Regex::new(\n        r\"Summary \\[\\s*([\\d.]+)s\\]\\s+(\\d+) tests? run:\\s+(\\d+) passed(?:,\\s+(\\d+) failed)?(?:,\\s+(\\d+) skipped)?\"\n    ).expect(\"invalid nextest summary regex\");\n    assert!(summary.is_match(\"Summary [ 2.345s] 42 tests run: 41 passed, 1 failed\"));\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Hoist every literal regex into a module-level `static X: LazyLock<Regex>` (the repo's own rule in .claude/rules/rust-patterns.md) so the pattern is written once and compiled once.","Pair each filter regex with a unit test that both compiles it and matches a real nextest output line, so pattern edits fail CI instead of the first `rtk cargo nextest` run.","When adapting a pattern for a new nextest output format, paste a captured fixture line into the test first, then change the regex until the test passes — never edit the pattern blind.","Remember the rust regex crate has no backreferences or lookaround; keep patterns to literals, classes, groups, and repetition."],"tags":["regex","cargo","nextest","panic","expect","compile-time-constant"],"backgroundTag":null,"analyzedSha":"d977e1c31621fe8704e6500ceeb9c7a0de2b6836","analyzedAt":"2026-08-16T05:40:46.291Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}