dgtlmoon/changedetection.io · warning · ValidationError

XPath not permitted in this field!

Error message

XPath not permitted in this field!

What it means

In ValidateCSSJSONXPATHInput.__call__, each non-empty line of the field is inspected: if it starts with '/' or 'xpath:' it is treated as an XPath expression. When the validator instance was constructed with allow_xpath=False, any XPath-looking input is rejected immediately with "XPath not permitted in this field!" before any parsing occurs.

Source

Thrown at changedetectionio/forms.py:696

        self.allow_xpath = allow_xpath
        self.allow_json = allow_json

    def __call__(self, form, field):

        if isinstance(field.data, str):
            data = [field.data]
        else:
            data = field.data

        for line in data:
        # Nothing to see here
            if not len(line.strip()):
                return

            # Does it look like XPath?
            if line.strip()[0] == '/' or line.strip().startswith('xpath:'):
                if not self.allow_xpath:
                    raise ValidationError("XPath not permitted in this field!")
                from lxml import etree, html
                import elementpath
                from changedetectionio.html_tools import SafeXPath3Parser, lxml_guard, lxml_html_parser
                line = line.replace('xpath:', '')

                try:
                    # Runs on a Flask request thread - must share the worker's lxml lock.
                    with lxml_guard():
                        tree = html.fromstring("<html></html>", parser=lxml_html_parser())
                        elementpath.select(tree, line.strip(), parser=SafeXPath3Parser)
                except elementpath.ElementPathError as e:
                    message = field.gettext('\'%(expression)s\' is not a valid XPath expression. (%(error)s)')
                    raise ValidationError(message % {'expression': line, 'error': str(e)})
                except:
                    raise ValidationError("A system-error occurred when validating your XPath expression")

            if line.strip().startswith('xpath1:'):
                if not self.allow_xpath:

View on GitHub (pinned to 5d9c7c6da7)

Solutions

  1. Move the XPath expression to a field that supports XPath (allow_xpath=True)
  2. Convert the expression to a CSS selector, e.g. '//div[@id="main"]' -> 'div#main'
  3. Remove the leading 'xpath:' prefix or leading '/' if it was accidental

Example fix

# before (in a CSS-only field)
//div[@id="main"]
# after
div#main
Defensive patterns

Strategy: type-guard

Validate before calling

def line_permitted(line: str, allow_xpath: bool) -> bool:
    s = line.strip()
    looks_like_xpath = s.startswith('/') or s.startswith('xpath:')
    return allow_xpath or not looks_like_xpath

Type guard

def is_xpath_line(line: str) -> bool:
    s = line.strip()
    return s.startswith('/') or s.startswith('xpath:')

Try / catch

from wtforms import ValidationError
try:
    form.validate()
except ValidationError as e:
    if 'XPath not permitted' in str(e):
        hint('Move the XPath expression to an XPath-capable field')

Prevention

When it happens

Trigger: Submitting a filter/processing rule value beginning with '/' or 'xpath:' (e.g. '//div[@id="x"]' or 'xpath://h1') to a field whose validator was created with allow_xpath=False (fields that only accept CSS/JSON paths).

Common situations: Pasting an XPath selector into a CSS-selector-only field, or using xpath: prefix in a form that was configured without XPath support (operator restriction or field type).

Related errors


AI-assisted analysis of dgtlmoon/changedetection.io@5d9c7c6da7 (2026-08-27). Data as JSON: /api/errors/03296383245ff3ee. Report an issue: GitHub.