dgtlmoon/changedetection.io · error
abort(404)
Error message
abort(404)
What it means
Straightforward abort(404) when building the per-watch data package zip: the uuid is not present in datastore.data['watching']. The watch lookup happens before any zip work, so this only means 'no such watch'.
Source
Thrown at changedetectionio/blueprint/ui/edit.py:412
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()
with zipfile.ZipFile(memory_file, 'w',
compression=zipfile.ZIP_DEFLATED,
compresslevel=8) as zipObj:
# Add the watch's JSON file if it exists
watch_json_path = os.path.join(watch.data_dir, 'watch.json')
if os.path.isfile(watch_json_path):
zipObj.write(watch_json_path,
arcname=os.path.join(uuid, 'watch.json'),
compress_type=zipfile.ZIP_DEFLATED,
compresslevel=8)
# Add all files in the watch data directory
if os.path.isdir(watch.data_dir):View on GitHub (pinned to 5d9c7c6da7)
Solutions
- List current watches (GET /api/watch) and use a valid uuid
- Refresh the UI tab — it may reference a deleted watch
- Confirm you are talking to the correct instance/datastore if watches were imported elsewhere
Example fix
# before
requests.get(f'{base}/edit/{bad_uuid}/get-data-package').raise_for_status()
# after
watch_uuids = requests.get(f'{base}/api/watch').json().keys()
assert uuid in watch_uuids, 'watch no longer exists'
requests.get(f'{base}/edit/{uuid}/get-data-package').raise_for_status() Defensive patterns
Strategy: validation
Validate before calling
import requests
def watch_exists(base, uuid, auth=None):
return uuid in requests.get(f'{base}/api/watch', auth=auth).json() Try / catch
try:
r = requests.get(f'{base}/edit/{uuid}/get-data-package')
r.raise_for_status()
except requests.HTTPError as e:
if e.response.status_code == 404:
raise KeyError(f'watch {uuid} not found') from e
raise Prevention
- Validate uuid against the watch list before API calls
- Purge cached uuids when watches are deleted/reimported
- Copy uuids exactly — they are case-sensitive
When it happens
Trigger: GET /edit/<uuid>/get-data-package with a deleted watch uuid, a typo'd uuid, or a uuid from a different datastore instance.
Common situations: Stale UI tabs/bookmarks pointing at deleted watches; scripts holding cached uuids after watches were removed and re-imported; uuid copied with extra characters or wrong case.
Related errors
- No Favicon available for {uuid}
- Processor '{processor_name}' does not provide difference dat
- abort(404)
- Processor '{processor_name}' does not support xlsx export
- Asset '{asset_name}' not found
AI-assisted analysis of dgtlmoon/changedetection.io@5d9c7c6da7 (2026-08-27).
Data as JSON: /api/errors/a2fc82f1f0d3dcd2.
Report an issue: GitHub.