Hmbown/CodeWhale · error

title regex pattern is valid

Error message

title regex pattern is valid

What it means

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.

Source

Thrown at crates/tui/src/tools/web/scrape.rs:34

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ScrapedSearchResult {
    pub title: String,
    pub url: String,
    pub snippet: Option<String>,
}

// Cached regex patterns for HTML parsing
static TITLE_RE: OnceLock<Regex> = OnceLock::new();
static SNIPPET_RE: OnceLock<Regex> = OnceLock::new();
static TAG_RE: OnceLock<Regex> = OnceLock::new();
static BING_RESULT_RE: OnceLock<Regex> = OnceLock::new();
static BING_TITLE_RE: OnceLock<Regex> = OnceLock::new();
static BING_SNIPPET_RE: OnceLock<Regex> = OnceLock::new();

fn get_title_re() -> &'static Regex {
    TITLE_RE.get_or_init(|| {
        Regex::new(r#"<a[^>]*class=\"result__a\"[^>]*href=\"([^\"]+)\"[^>]*>(.*?)</a>"#)
            .expect("title regex pattern is valid")
    })
}

fn get_snippet_re() -> &'static Regex {
    SNIPPET_RE.get_or_init(|| {
        Regex::new(
            r#"<a[^>]*class=\"result__snippet\"[^>]*>(.*?)</a>|<div[^>]*class=\"result__snippet\"[^>]*>(.*?)</div>"#,
        )
        .expect("snippet regex pattern is valid")
    })
}

fn get_tag_re() -> &'static Regex {
    TAG_RE.get_or_init(|| Regex::new(r"<[^>]+>").expect("tag regex pattern is valid"))
}

fn get_bing_result_re() -> &'static Regex {
    BING_RESULT_RE.get_or_init(|| {

View on GitHub (pinned to 0c42157ee5)

Solutions

  1. Fix the pattern syntax: validate it with the Rust regex flavor (a scratch `Regex::new(pat)` test or an online tester set to Rust).
  2. Add/keep a unit test that forces initialization of all six getters so edits fail in CI, not at scrape time.
  3. Run `scripts/dev-test.sh crates/tui/src/tools/web/scrape.rs` (or `cargo test -p codewhale-tui --lib scrape`) after touching the pattern.

Example fix

// before
Regex::new(r#"<a[^>]*class=\"result__a\"[^>]*href=\"([^\"]+)\"[^>]*>(.*?)</a>"#)
    .expect("title regex pattern is valid")

// after: pin every literal with a compile test so edits fail in CI
#[test]
fn scrape_patterns_compile() {
    let _ = get_title_re();
    let _ = get_snippet_re();
    let _ = get_tag_re();
    let _ = get_bing_result_re();
    let _ = get_bing_title_re();
    let _ = get_bing_snippet_re();
}
Defensive patterns

Strategy: validation

Validate before calling

// Force-compile all scrape patterns before relying on them in a session
#[test]
fn scrape_patterns_compile() {
    let _ = get_title_re();
    let _ = get_snippet_re();
    let _ = get_tag_re();
    let _ = get_bing_result_re();
    let _ = get_bing_title_re();
    let _ = get_bing_snippet_re();
}

Try / catch

let results = std::panic::catch_unwind(|| parse_duckduckgo_results(&html, max_results))
    .unwrap_or_default();

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@0c42157ee5 (2026-08-20). Data as JSON: /api/errors/98a174f43b278105. Report an issue: GitHub.