{"record":{"id":"b497aeac10327640","repo":"D4Vinci/Scrapling","slug":"invalid-css-selector-selector-str-e","errorCode":null,"errorMessage":"Invalid CSS selector '{selector}': {str(e)}","messagePattern":"Invalid CSS selector '(.+?)': (.+?)","errorType":"exception","errorClass":"SelectorSyntaxError","httpStatus":null,"severity":"error","filePath":"scrapling/parser.py","lineNumber":624,"sourceCode":"            results = Selectors()\n            for single_selector in split_selectors(selector):\n                # I'm doing this only so the `save` function saves data correctly for combined selectors\n                # Like using the ',' to combine two different selectors that point to different elements.\n                xpath_selector = _css_to_xpath(single_selector.canonical())\n                results += self.xpath(\n                    xpath_selector,\n                    identifier or single_selector.canonical(),\n                    adaptive,\n                    auto_save,\n                    percentage,\n                )\n\n            return Selectors(results)\n        except (\n            SelectorError,\n            SelectorSyntaxError,\n        ) as e:\n            raise SelectorSyntaxError(f\"Invalid CSS selector '{selector}': {str(e)}\") from e\n\n    def xpath(\n        self,\n        selector: str,\n        identifier: str = \"\",\n        adaptive: bool = False,\n        auto_save: bool = False,\n        percentage: int = 40,\n        **kwargs: Any,\n    ) -> \"Selectors\":\n        \"\"\"Search the current tree with XPath selectors\n\n        **Important:\n        It's recommended to use the identifier argument if you plan to use a different selector later\n        and want to relocate the same element(s)**\n\n         Note: **Additional keyword arguments will be passed as XPath variables in the XPath expression!**\n","sourceCodeStart":606,"sourceCodeEnd":642,"githubUrl":"https://github.com/D4Vinci/Scrapling/blob/5d213a2d4764002bfc4fed33c32fe09fa8b0bf7f/scrapling/parser.py#L606-L642","documentation":"The css() method failed to compile or evaluate the CSS selector string. scrapling translates the CSS expression with cssselect and wraps any SelectorError/SelectorSyntaxError into scrapling's SelectorSyntaxError, chaining the original exception so the underlying reason is preserved in __cause__.","triggerScenarios":"Calling selector.css('<bad expr>') with malformed syntax such as unbalanced parentheses 'div(', pseudo-classes unsupported by cssselect like ':has-text(foo)', or typos like 'di..v'.","commonSituations":"Porting selectors from browser DevTools or Selenium that use JavaScript-only pseudo-classes; dynamically building selector strings from user input where a variable is empty; copy-paste typos.","solutions":["Read the chained message: str(e.__cause__) gives the exact cssselect parse error and position.","Fix the expression — verify it in DevTools but strip JS-only pseudo-classes (:has-text, :contains in some engines) before using it in scrapling.","For text matching needs, switch to xpath() with contains(text(), ...) instead of unsupported CSS pseudo-classes.","If selectors come from config/user input, validate them once at startup with cssselect.GenericTranslator().css_to_xpath(expr) and fail fast."],"exampleFix":"# before\npage.css('a:has-text(\"Next\")')  # SelectorSyntaxError\n\n# after\npage.xpath('//a[contains(text(), \"Next\")]')","handlingStrategy":"try-catch","validationCode":"from cssselect import GenericTranslator\n\ndef css_is_valid(expr: str) -> bool:\n    try:\n        GenericTranslator().css_to_xpath(expr)\n        return True\n    except Exception:\n        return False\n\n# validate user/config-provided selectors at startup\nassert css_is_valid(selector), f'bad selector from config: {selector!r}'","typeGuard":"def is_selector_str(s: str) -> bool:\n    return isinstance(s, str) and len(s.strip()) > 0 and not any(\n        p in s for p in (':has-text', ':contains', ':matches-css')\n    )","tryCatchPattern":"from scrapling.core.exceptions import SelectorSyntaxError\n\ntry:\n    items = page.css(selector)\nexcept SelectorSyntaxError as e:\n    logger.error('bad css selector %r: %s', selector, e.__cause__)\n    items = page.css('a')  # or re-raise / skip this selector","preventionTips":["Validate config-driven selectors once at startup with cssselect instead of per-request.","Do not copy JS-only pseudo-classes from DevTools into scrapling selectors.","Log e.__cause__ — it carries the precise cssselect parse error and position."],"tags":["css","selector","cssselect","validation"],"backgroundTag":null,"analyzedSha":"5d213a2d4764002bfc4fed33c32fe09fa8b0bf7f","analyzedAt":"2026-08-14T22:23:09.440Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}