D4Vinci/Scrapling · error · SelectorSyntaxError
Invalid CSS selector '{selector}': {str(e)}
Error message
Invalid CSS selector '{selector}': {str(e)} What it means
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__.
Source
Thrown at scrapling/parser.py:624
results = Selectors()
for single_selector in split_selectors(selector):
# I'm doing this only so the `save` function saves data correctly for combined selectors
# Like using the ',' to combine two different selectors that point to different elements.
xpath_selector = _css_to_xpath(single_selector.canonical())
results += self.xpath(
xpath_selector,
identifier or single_selector.canonical(),
adaptive,
auto_save,
percentage,
)
return Selectors(results)
except (
SelectorError,
SelectorSyntaxError,
) as e:
raise SelectorSyntaxError(f"Invalid CSS selector '{selector}': {str(e)}") from e
def xpath(
self,
selector: str,
identifier: str = "",
adaptive: bool = False,
auto_save: bool = False,
percentage: int = 40,
**kwargs: Any,
) -> "Selectors":
"""Search the current tree with XPath selectors
**Important:
It's recommended to use the identifier argument if you plan to use a different selector later
and want to relocate the same element(s)**
Note: **Additional keyword arguments will be passed as XPath variables in the XPath expression!**
View on GitHub (pinned to 5d213a2d47)
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.
Example fix
# before
page.css('a:has-text("Next")') # SelectorSyntaxError
# after
page.xpath('//a[contains(text(), "Next")]') Defensive patterns
Strategy: try-catch
Validate before calling
from cssselect import GenericTranslator
def css_is_valid(expr: str) -> bool:
try:
GenericTranslator().css_to_xpath(expr)
return True
except Exception:
return False
# validate user/config-provided selectors at startup
assert css_is_valid(selector), f'bad selector from config: {selector!r}' Type guard
def is_selector_str(s: str) -> bool:
return isinstance(s, str) and len(s.strip()) > 0 and not any(
p in s for p in (':has-text', ':contains', ':matches-css')
) Try / catch
from scrapling.core.exceptions import SelectorSyntaxError
try:
items = page.css(selector)
except SelectorSyntaxError as e:
logger.error('bad css selector %r: %s', selector, e.__cause__)
items = page.css('a') # or re-raise / skip this selector Prevention
- 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.
When it happens
Trigger: 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'.
Common situations: 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.
Related errors
- Selector class needs HTML content, or root arguments to work
- content argument must be str or bytes, got {type(content)}
- Invalid XPath selector: {selector}
- 'quality' is only valid when 'image_type' is 'jpeg'.
- Unknown extraction type: {extraction_type}
AI-assisted analysis of D4Vinci/Scrapling@5d213a2d47 (2026-08-14).
Data as JSON: /api/errors/b497aeac10327640.
Report an issue: GitHub.