dgtlmoon/changedetection.io · error · ValueError
Backup archive decompressed size ({total_uncompressed // (10
Error message
Backup archive decompressed size ({total_uncompressed // (1024 * 1024)} MB) exceeds the {_MAX_DECOMPRESSED_BYTES // (1024 * 1024)} MB limit What it means
The multi-line expression validator supports JSONPath only in fields explicitly flagged allow_json. When a submitted line contains 'json:' and the field was constructed with allow_json=False, validation rejects it immediately with this message.
Source
Thrown at changedetectionio/blueprint/backups/restore.py:66
Returns a dict with counts: restored_groups, skipped_groups, restored_watches, skipped_watches.
Raises zipfile.BadZipFile if the stream is not a valid zip.
"""
from changedetectionio.model import Tag
restored_groups = 0
skipped_groups = 0
restored_watches = 0
skipped_watches = 0
current_tags = datastore.data['settings']['application'].get('tags', {})
current_watches = datastore.data['watching']
with tempfile.TemporaryDirectory() as tmpdir:
logger.debug(f"Restore: extracting zip to {tmpdir}")
with zipfile.ZipFile(zip_stream, 'r') as zf:
total_uncompressed = sum(m.file_size for m in zf.infolist())
if total_uncompressed > _MAX_DECOMPRESSED_BYTES:
raise ValueError(
f"Backup archive decompressed size ({total_uncompressed // (1024 * 1024)} MB) "
f"exceeds the {_MAX_DECOMPRESSED_BYTES // (1024 * 1024)} MB limit"
)
resolved_dest = os.path.realpath(tmpdir)
for member in zf.infolist():
member_dest = os.path.realpath(os.path.join(resolved_dest, member.filename))
if not member_dest.startswith(resolved_dest + os.sep) and member_dest != resolved_dest:
raise ValueError(f"Zip Slip path traversal detected in backup archive: {member.filename!r}")
zf.extract(member, tmpdir)
logger.debug("Restore: zip extracted, scanning UUID directories")
for entry in os.scandir(tmpdir):
if not entry.is_dir():
continue
uuid = entry.name
if not _UUID_RE.match(uuid):
logger.warning(f"Restore: skipping non-UUID directory {uuid!r}")View on GitHub (pinned to 5d9c7c6da7)
Solutions
- Move the json: expression to a field that permits JSONPath (the extract_text / JSON-capable field)
- If building a custom form, pass allow_json=True to the validator
- Strip the 'json:' prefix if you intended a plain XPath
Example fix
# before # field: css filter (XPath only) -> json:$.a.b # after # field: 'Extract text before/after or JSON' -> json:$.a.b
Defensive patterns
Strategy: validation
Validate before calling
def line_allowed(line: str, allow_json: bool) -> bool:
if 'json:' in line:
return allow_json
return True Type guard
def is_jsonpath_line(line: str) -> bool:
return line.strip().lower().startswith('json:') Prevention
- Enter json: rules only in fields documented as JSON-capable
- When building forms, explicitly decide allow_json per field
- Disable/hide JSON inputs in the UI when allow_json=False
When it happens
Trigger: Entering 'json:$.store.book[0].title' into a field whose ValidateMultiDataUrl/validator instance was created without allow_json=True (e.g. the CSS/filter XPath-only field instead of the 'Extract/Convert' text field).
Common situations: Users pasting JSON extraction rules into the wrong form field; plugin/custom watch forms that reuse the validator without enabling JSON support; UI not making clear which fields accept json: prefixes.
Related errors
- Invalid JSON object for field: {value}
- Zip Slip path traversal detected in backup archive: {member.
- BrowserStepsStepException
- One of the 'conditions' rulesets is incomplete, cannot run.
- EmptyReply
AI-assisted analysis of dgtlmoon/changedetection.io@5d9c7c6da7 (2026-08-27).
Data as JSON: /api/errors/c9eb5e95f9a810dd.
Report an issue: GitHub.