dgtlmoon/changedetection.io · error
abort(500)
Error message
abort(500)
What it means
A catch-all abort(500) at the end of the 'get latest rendered HTML' endpoint. The happy path decompresses the latest snapshot (e.g. zlib/gzip) and streams it; if fetching/decompressing the snapshot produced nothing usable, execution falls through to the 500. It signals a corrupt or unreadable latest snapshot rather than a missing one (missing watches 404 earlier).
Source
Thrown at changedetectionio/blueprint/ui/edit.py:398
if uuid == 'first':
uuid = list(datastore.data['watching'].keys()).pop()
watch = datastore.data['watching'].get(uuid)
if watch and watch.history.keys() and os.path.isdir(watch.data_dir):
latest_filename = list(watch.history.keys())[-1]
html_fname = os.path.join(watch.data_dir, f"{latest_filename}.html.br")
with open(html_fname, 'rb') as f:
if html_fname.endswith('.br'):
# Read and decompress the Brotli file
decompressed_data = brotli.decompress(f.read())
else:
decompressed_data = f.read()
buffer = BytesIO(decompressed_data)
return send_file(buffer, as_attachment=True, download_name=f"{latest_filename}.html", mimetype='text/html')
# Return a 500 error
abort(500)
@edit_blueprint.route("/edit/<uuid_str:uuid>/get-data-package", methods=['GET'])
@login_optionally_required
def watch_get_data_package(uuid):
"""Download all data for a single watch as a zip file"""
from io import BytesIO
from flask import send_file
import zipfile
from pathlib import Path
import datetime
watch = datastore.data['watching'].get(uuid)
if not watch:
abort(404)
# Create zip in memory
memory_file = BytesIO()
View on GitHub (pinned to 5d9c7c6da7)
Solutions
- Run a recheck for the watch so a fresh snapshot replaces the corrupt latest one
- Inspect the watch's data_dir snapshot files and delete the corrupt latest snapshot
- Restore the datastore from backup if multiple watches are affected
- If it persists, report with the watch's snapshot file for compression-format investigation
Example fix
# before
resp = requests.get(f'{base}/edit/{uuid}/get-html')
assert resp.status_code == 200
# after
resp = requests.get(f'{base}/edit/{uuid}/get-html')
if resp.status_code == 500:
requests.get(f'{base}/checknow', params={'uuid': uuid}) # regenerate snapshot
resp = requests.get(f'{base}/edit/{uuid}/get-html')
resp.raise_for_status() Defensive patterns
Strategy: retry
Validate before calling
# no client-side precheck exists; verify snapshot health indirectly
import requests
def snapshot_ok(base, uuid):
return requests.get(f'{base}/edit/{uuid}/get-html').status_code == 200 Try / catch
try:
r = requests.get(f'{base}/edit/{uuid}/get-html')
r.raise_for_status()
except requests.HTTPError as e:
if e.response.status_code == 500:
requests.get(f'{base}/checknow', params={'uuid': uuid})
r = requests.get(f'{base}/edit/{uuid}/get-html')
r.raise_for_status()
else:
raise Prevention
- Avoid killing the app mid-write to prevent truncated snapshots
- Keep regular datastore backups
- Re-run checks after restoring or migrating data
When it happens
Trigger: GET /edit/<uuid>/get-html when the stored latest snapshot is empty or fails decompression (truncated write, wrong compression headers, disk corruption), or the watch has a history entry whose payload cannot be decompressed.
Common situations: Datastore corruption after a crash mid-write; snapshots written by an older version using a different compression; disk-full events truncating snapshot files; empty history from a failed first check.
Related errors
- No Favicon available for {uuid}
- abort(400) # Bad Request if the filename doesn't match the
- abort(404)
- Processor '{processor_name}' does not provide difference dat
- Processor '{processor_name}' does not support xlsx export
AI-assisted analysis of dgtlmoon/changedetection.io@5d9c7c6da7 (2026-08-27).
Data as JSON: /api/errors/9f1f2f0dbac8f679.
Report an issue: GitHub.