dgtlmoon/changedetection.io · error · BadRequest
Validation failed: {'; '.join(error_details)}
Error message
Validation failed: {'; '.join(error_details)} What it means
A catch-all bare `except:` in the XPath validator catches any non-XPathEvalError exception thrown while compiling the expression (or while acquiring the lxml lock / building the parser). It signals an unexpected system-level failure rather than a pure syntax error, and masks the original exception's details.
Source
Thrown at changedetectionio/api/__init__.py:214
# match the OpenAPI server definitions, causing false positives.
if isinstance(error, PathError):
logger.debug(f"API Call - Skipping path/server validation (delegated to Flask): {error}")
continue
error_str = str(error)
# Extract detailed schema errors from __cause__
if hasattr(error, '__cause__') and hasattr(error.__cause__, 'schema_errors'):
for schema_error in error.__cause__.schema_errors:
field = '.'.join(str(p) for p in schema_error.path) if schema_error.path else 'body'
msg = schema_error.message if hasattr(schema_error, 'message') else str(schema_error)
error_details.append(f"{field}: {msg}")
else:
error_details.append(error_str)
# Only raise if we have actual validation errors (not path/server issues)
if error_details:
logger.error(f"API Call - Validation failed: {'; '.join(error_details)}")
raise BadRequest(f"Validation failed: {'; '.join(error_details)}")
except BadRequest:
# Re-raise BadRequest exceptions (validation failures)
raise
except Exception as e:
# If OpenAPI spec loading fails, log but don't break existing functionality
logger.critical(f"OpenAPI validation warning for {operation_id}: {e}")
abort(500)
return f(*args, **kwargs)
return wrapper
return decorator
# Import all API resources
from .Watch import Watch, WatchHistory, WatchSingleHistory, WatchHistoryDiff, CreateWatch, WatchFavicon
from .Tags import Tags, Tag
from .Import import Import
from .SystemInfo import SystemInfo
from .Spec import Spec
from .Notifications import NotificationsView on GitHub (pinned to 5d9c7c6da7)
Solutions
- Check server logs / reproduce the compile locally to find the real exception
- Verify lxml is properly installed and version-matched (pip install -U lxml) and the parser is not shared unsafely across threads
- If you control the code, log the real exception before raising the generic message
Example fix
null
Defensive patterns
Strategy: try-catch
Validate before calling
null
Try / catch
try:
tree.xpath(expr)
except etree.XPathEvalError:
handle_syntax_error()
except Exception as e:
log.exception('unexpected lxml failure')
handle_system_error(e) Prevention
- Pin a known-good lxml version
- Don't share a single parser object across threads without a lock
- Log the real exception; bare except: hides root causes
When it happens
Trigger: lxml raising MemoryError, KeyboardInterrupt-style exceptions, parser instantiation failure, or lxml_guard lock errors while tree.xpath(line) runs; any unexpected exception type other than etree.XPathEvalError escaping the compile step.
Common situations: Broken lxml installation or version mismatch where html.fromstring or the shared parser misbehaves; concurrency issues around the shared lxml parser; thread interruption during validation. Rare in practice; almost all bad input hits the XPathEvalError branch instead.
Related errors
- A system-error occurred when validating your XPath expressio
- Invalid JSON object for field: {value}
- One of the 'conditions' rulesets is incomplete, cannot run.
- Content Fetcher > xPath scraper failed. Please report this U
- XPath not permitted in this field!
AI-assisted analysis of dgtlmoon/changedetection.io@5d9c7c6da7 (2026-08-27).
Data as JSON: /api/errors/18e0adc3ed82aeae.
Report an issue: GitHub.