{"record":{"id":"73e77b34983d7719","repo":"sigoden/aichat","slug":"invalid-extract-selector","errorCode":null,"errorMessage":"Invalid extract selector, {}","messagePattern":"Invalid extract selector, (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"src/utils/request.rs","lineNumber":414,"sourceCode":"    for element in document.select(&selector) {\n        if let Some(href) = element.value().attr(\"href\") {\n            let href = Url::parse(href).ok().or_else(|| location.join(href).ok());\n            match href {\n                None => continue,\n                Some(href) => {\n                    if href.as_str().starts_with(location.as_str())\n                        && !should_exclude_link(href.path(), &options.exclude)\n                    {\n                        links.insert(href.path().to_string());\n                    }\n                }\n            }\n        }\n    }\n\n    let text = if let Some(selector) = &options.extract {\n        let selector = Selector::parse(selector)\n            .map_err(|err| anyhow!(\"Invalid extract selector, {}\", err))?;\n        document\n            .select(&selector)\n            .map(|v| html_to_md(&v.html()))\n            .collect::<Vec<String>>()\n            .join(\"\\n\\n\")\n    } else {\n        html_to_md(&body)\n    };\n\n    Ok((path.to_string(), text, links.into_iter().collect()))\n}\n\nfn should_exclude_link(link: &str, exclude: &[String]) -> bool {\n    if link.contains(\"#\") {\n        return true;\n    }\n    let parts: Vec<&str> = link.trim_end_matches('/').split('/').collect();\n    let name = parts.last().unwrap_or(&\"\").to_lowercase();","sourceCodeStart":396,"sourceCodeEnd":432,"githubUrl":"https://github.com/sigoden/aichat/blob/82976d349ad97ac9aae0655ad631dace5e2a6385/src/utils/request.rs#L396-L432","documentation":"When crawl options include an `extract` CSS selector, `crawl_page` parses it with `scraper::Selector::parse` and returns `anyhow!(\"Invalid extract selector, {}\", err)` if parsing fails. The error includes the underlying selector-syntax error, so the selector used for extracting text from the crawled HTML is invalid.","triggerScenarios":"Passing a syntactically invalid CSS selector in `options.extract` to `crawl_website`/`crawl_page`, e.g. `div[foo=` (unclosed attribute), empty string, or unsupported pseudo-classes.","commonSituations":"Hand-written selectors in config with typos or unbalanced brackets/quotes; selectors copied from XPath or jQuery that aren't valid CSS (e.g. `//div[@id]`); scraper crate not supporting certain pseudo-elements.","solutions":["Validate the selector in the browser devtools (`document.querySelector(...)`) before putting it in config.","Fix syntax errors reported in the message: unbalanced brackets, quotes, or parentheses.","Convert XPath/jQuery expressions to CSS selector syntax.","Pre-test with `Selector::parse(...)` at config-load time to fail fast with a clear message."],"exampleFix":"// before\nlet options = CrawlOptions { extract: Some(\"//article[@class='post']\".into()), ..Default::default() };\n// after\nlet options = CrawlOptions { extract: Some(\"article.post\".into()), ..Default::default() };","handlingStrategy":"validation","validationCode":"fn validate_selector(sel: &str) -> Result<(), String> {\n    match scraper::Selector::parse(sel) {\n        Ok(_) => Ok(()),\n        Err(e) => Err(format!(\"invalid extract selector {sel:?}: {e}\")),\n    }\n}\n// call at config load: validate_selector(options.extract.as_deref().unwrap_or(\"*\"))?;","typeGuard":null,"tryCatchPattern":"match crawl_website(&url, options).await {\n    Err(e) if e.to_string().starts_with(\"Invalid extract selector\") => {\n        eprintln!(\"Fix --extract: {e}\");\n    }\n    other => other?,\n}","preventionTips":["Test selectors in browser devtools with document.querySelector before configuring.","Use CSS selector syntax only — convert XPath/jQuery expressions.","Validate the extract selector once at startup, not per crawl.","Keep brackets/quotes balanced; prefer simple class/id selectors."],"tags":["css-selector","crawler","html","validation"],"backgroundTag":"invalid-regex-pattern","analyzedSha":"82976d349ad97ac9aae0655ad631dace5e2a6385","analyzedAt":"2026-09-09T18:33:06.139Z","contentChangedAt":"2026-09-09T18:33:06.139Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}