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
- Move the XPath expression to a field that supports XPath (allow_xpath=True)
- Convert the expression to a CSS selector, e.g. '//div[@id="main"]' -> 'div#main'
- 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
- Know which fields accept XPath before pasting selectors
- Prefer CSS selectors where possible
- Keep a field-type cheat sheet in the UI
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
- '%(expression)s' is not a valid XPath expression. (%(error)s
- Invalid JSON object for field: {value}
- A variable or function is not defined: {e}
- The following tokens used in the notification are not valid:
- Watch protocol is not permitted or invalid URL format
AI-assisted analysis of dgtlmoon/changedetection.io@5d9c7c6da7 (2026-08-27).
Data as JSON: /api/errors/03296383245ff3ee.
Report an issue: GitHub.