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

  1. Read the chained message: str(e.__cause__) gives the exact cssselect parse error and position.
  2. Fix the expression — verify it in DevTools but strip JS-only pseudo-classes (:has-text, :contains in some engines) before using it in scrapling.
  3. For text matching needs, switch to xpath() with contains(text(), ...) instead of unsupported CSS pseudo-classes.
  4. 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

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


AI-assisted analysis of D4Vinci/Scrapling@5d213a2d47 (2026-08-14). Data as JSON: /api/errors/b497aeac10327640. Report an issue: GitHub.