{"record":{"id":"98a174f43b278105","repo":"Hmbown/CodeWhale","slug":"title-regex-pattern-is-valid","errorCode":null,"errorMessage":"title regex pattern is valid","messagePattern":"title regex pattern is valid","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/tools/web/scrape.rs","lineNumber":34,"sourceCode":"#[derive(Debug, Clone, PartialEq, Eq)]\npub struct ScrapedSearchResult {\n    pub title: String,\n    pub url: String,\n    pub snippet: Option<String>,\n}\n\n// Cached regex patterns for HTML parsing\nstatic TITLE_RE: OnceLock<Regex> = OnceLock::new();\nstatic SNIPPET_RE: OnceLock<Regex> = OnceLock::new();\nstatic TAG_RE: OnceLock<Regex> = OnceLock::new();\nstatic BING_RESULT_RE: OnceLock<Regex> = OnceLock::new();\nstatic BING_TITLE_RE: OnceLock<Regex> = OnceLock::new();\nstatic BING_SNIPPET_RE: OnceLock<Regex> = OnceLock::new();\n\nfn get_title_re() -> &'static Regex {\n    TITLE_RE.get_or_init(|| {\n        Regex::new(r#\"<a[^>]*class=\\\"result__a\\\"[^>]*href=\\\"([^\\\"]+)\\\"[^>]*>(.*?)</a>\"#)\n            .expect(\"title regex pattern is valid\")\n    })\n}\n\nfn get_snippet_re() -> &'static Regex {\n    SNIPPET_RE.get_or_init(|| {\n        Regex::new(\n            r#\"<a[^>]*class=\\\"result__snippet\\\"[^>]*>(.*?)</a>|<div[^>]*class=\\\"result__snippet\\\"[^>]*>(.*?)</div>\"#,\n        )\n        .expect(\"snippet regex pattern is valid\")\n    })\n}\n\nfn get_tag_re() -> &'static Regex {\n    TAG_RE.get_or_init(|| Regex::new(r\"<[^>]+>\").expect(\"tag regex pattern is valid\"))\n}\n\nfn get_bing_result_re() -> &'static Regex {\n    BING_RESULT_RE.get_or_init(|| {","sourceCodeStart":16,"sourceCodeEnd":52,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/0c42157ee52f9d55af2b506d71b46249910f77d3/crates/tui/src/tools/web/scrape.rs#L16-L52","documentation":"Panic compiling the DuckDuckGo title pattern (`<a[^>]*class=\"result__a\"[^>]*href=\"([^\"]+)\"...`) on first use via `TITLE_RE.get_or_init`. The `regex` crate returns `Err` for syntactically invalid patterns; because the pattern is a hardcoded literal verified the first time it runs, this expect fires only after someone edits the literal into an invalid regex (unbalanced group, bad escape, stray quantifier). On panic the `OnceLock` stays uninitialized, so the next call retries initialization.","triggerScenarios":"A code change to the literal that breaks regex syntax; the first `parse_duckduckgo_results` call after that build panics inside `get_or_init` while scraping or testing.","commonSituations":"Adjusting the DDG HTML selector without running the scrape tests; copy-pasting a PCRE/JavaScript construct the regex crate rejects (backreferences, lookaround) into the literal.","solutions":["Fix the pattern syntax: validate it with the Rust regex flavor (a scratch `Regex::new(pat)` test or an online tester set to Rust).","Add/keep a unit test that forces initialization of all six getters so edits fail in CI, not at scrape time.","Run `scripts/dev-test.sh crates/tui/src/tools/web/scrape.rs` (or `cargo test -p codewhale-tui --lib scrape`) after touching the pattern."],"exampleFix":"// before\nRegex::new(r#\"<a[^>]*class=\\\"result__a\\\"[^>]*href=\\\"([^\\\"]+)\\\"[^>]*>(.*?)</a>\"#)\n    .expect(\"title regex pattern is valid\")\n\n// after: pin every literal with a compile test so edits fail in CI\n#[test]\nfn scrape_patterns_compile() {\n    let _ = get_title_re();\n    let _ = get_snippet_re();\n    let _ = get_tag_re();\n    let _ = get_bing_result_re();\n    let _ = get_bing_title_re();\n    let _ = get_bing_snippet_re();\n}","handlingStrategy":"validation","validationCode":"// Force-compile all scrape patterns before relying on them in a session\n#[test]\nfn scrape_patterns_compile() {\n    let _ = get_title_re();\n    let _ = get_snippet_re();\n    let _ = get_tag_re();\n    let _ = get_bing_result_re();\n    let _ = get_bing_title_re();\n    let _ = get_bing_snippet_re();\n}","typeGuard":null,"tryCatchPattern":"let results = std::panic::catch_unwind(|| parse_duckduckgo_results(&html, max_results))\n    .unwrap_or_default();","preventionTips":["Validate edited patterns against the Rust regex flavor — no backreferences, no lookaround.","Run the scrape tests after every pattern edit.","Keep SERP patterns as untouched literals in release branches."],"tags":["rust","regex","duckduckgo","html-scraping","panic","expect"],"backgroundTag":"invalid-regex-pattern","analyzedSha":"0c42157ee52f9d55af2b506d71b46249910f77d3","analyzedAt":"2026-08-20T21:50:45.477Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}