dgtlmoon/changedetection.io · error · ValueError

Invalid JSON object for field: {value}

Error message

Invalid JSON object for field: {value}

What it means

This ValidationError is raised by the XPath validator in changedetectionio when lxml's tree.xpath() raises XPathEvalError while compiling a user-supplied XPath expression. It means the expression is syntactically invalid per the XPath 1.0 grammar that lxml/libxml2 supports. The message embeds both the bad expression and the underlying libxml2 error string.

Source

Thrown at changedetectionio/api/Import.py:78

        else:
            prop_type = None

    # Handle array type (e.g., notification_urls)
    if prop_type == 'array':
        # Support both comma-separated and JSON array format
        if value.startswith('['):
            try:
                return json.loads(value)
            except json.JSONDecodeError:
                return [v.strip() for v in value.split(',')]
        return [v.strip() for v in value.split(',')]

    # Handle object type (e.g., time_between_check, headers)
    elif prop_type == 'object':
        try:
            return json.loads(value)
        except json.JSONDecodeError:
            raise ValueError(f"Invalid JSON object for field: {value}")

    # Handle boolean type
    elif prop_type == 'boolean':
        return strtobool(value)

    # Handle integer type
    elif prop_type == 'integer':
        return int(value)

    # Handle number type (float)
    elif prop_type == 'number':
        return float(value)

    # Default: return as string
    return value


class Import(Resource):

View on GitHub (pinned to 5d9c7c6da7)

Solutions

  1. Fix the XPath syntax as reported in the embedded %(error)s text from libxml2
  2. Wrap browser-copied expressions: //tag[@class='x'] instead of CSS selectors
  3. Use 'css:' prefix in changedetectionio if you want CSS selectors instead of raw XPath
  4. Test the expression in a REPL: lxml.html.fromstring('<html></html>').xpath(expr)

Example fix

# before
//div[@class='item  # missing closing bracket
# after
//div[@class='item']
Defensive patterns

Strategy: validation

Validate before calling

from lxml import html, etree

def xpath_ok(expr: str) -> bool:
    try:
        tree = html.fromstring('<html></html>')
        v = tree.xpath(expr)
        return isinstance(v, list)
    except (etree.XPathEvalError, etree.XPathSyntaxError):
        return False
    except Exception:
        return False

Type guard

def is_valid_xpath(expr: str) -> bool:
    try:
        html.fromstring('<html></html>').xpath(expr)
        return True
    except etree.XPathError:
        return False

Try / catch

try:
    form.validate()
except ValidationError as e:
    if 'not a valid XPath' in str(e):
        # show embedded libxml2 detail to the user
        ...

Prevention

When it happens

Trigger: Submitting a watch/filter form where a CSS/XPath-like field contains a line that lxml fails to compile, e.g. 'div[' (unclosed bracket), '///text()', or unsupported XPath 2.0 syntax like 'matches(...)' or 'if/then/else'. Each newline-separated line is stripped and compiled against an empty <html> doc under an lxml lock in forms.py __call__.

Common situations: Copy-pasting browser DevTools 'Copy XPath' output that contains quotes/newlines; using XPath 2.0+ functions unsupported by libxml2 (matches, lowercase, string-join); typos in filters like missing closing brackets; pasting CSS selectors (div.foo) instead of XPath (//div[@class='foo']).

Understand the failure class

Related errors


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