{"record":{"id":"9f1f2f0dbac8f679","repo":"dgtlmoon/changedetection.io","slug":"abort-500","errorCode":null,"errorMessage":"abort(500)","messagePattern":"abort\\(500\\)","errorType":"http","errorClass":null,"httpStatus":500,"severity":"error","filePath":"changedetectionio/blueprint/ui/edit.py","lineNumber":398,"sourceCode":"        if uuid == 'first':\n            uuid = list(datastore.data['watching'].keys()).pop()\n        watch = datastore.data['watching'].get(uuid)\n        if watch and watch.history.keys() and os.path.isdir(watch.data_dir):\n            latest_filename = list(watch.history.keys())[-1]\n            html_fname = os.path.join(watch.data_dir, f\"{latest_filename}.html.br\")\n            with open(html_fname, 'rb') as f:\n                if html_fname.endswith('.br'):\n                    # Read and decompress the Brotli file\n                    decompressed_data = brotli.decompress(f.read())\n                else:\n                    decompressed_data = f.read()\n\n            buffer = BytesIO(decompressed_data)\n\n            return send_file(buffer, as_attachment=True, download_name=f\"{latest_filename}.html\", mimetype='text/html')\n\n        # Return a 500 error\n        abort(500)\n\n    @edit_blueprint.route(\"/edit/<uuid_str:uuid>/get-data-package\", methods=['GET'])\n    @login_optionally_required\n    def watch_get_data_package(uuid):\n        \"\"\"Download all data for a single watch as a zip file\"\"\"\n        from io import BytesIO\n        from flask import send_file\n        import zipfile\n        from pathlib import Path\n        import datetime\n\n        watch = datastore.data['watching'].get(uuid)\n        if not watch:\n            abort(404)\n\n        # Create zip in memory\n        memory_file = BytesIO()\n","sourceCodeStart":380,"sourceCodeEnd":416,"githubUrl":"https://github.com/dgtlmoon/changedetection.io/blob/5d9c7c6da76340597243e8163c4f2439237fa0e8/changedetectionio/blueprint/ui/edit.py#L380-L416","documentation":"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).","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"# before\nresp = requests.get(f'{base}/edit/{uuid}/get-html')\nassert resp.status_code == 200\n\n# after\nresp = requests.get(f'{base}/edit/{uuid}/get-html')\nif resp.status_code == 500:\n    requests.get(f'{base}/checknow', params={'uuid': uuid})  # regenerate snapshot\n    resp = requests.get(f'{base}/edit/{uuid}/get-html')\nresp.raise_for_status()","handlingStrategy":"retry","validationCode":"# no client-side precheck exists; verify snapshot health indirectly\nimport requests\ndef snapshot_ok(base, uuid):\n    return requests.get(f'{base}/edit/{uuid}/get-html').status_code == 200","typeGuard":null,"tryCatchPattern":"try:\n    r = requests.get(f'{base}/edit/{uuid}/get-html')\n    r.raise_for_status()\nexcept requests.HTTPError as e:\n    if e.response.status_code == 500:\n        requests.get(f'{base}/checknow', params={'uuid': uuid})\n        r = requests.get(f'{base}/edit/{uuid}/get-html')\n        r.raise_for_status()\n    else:\n        raise","preventionTips":["Avoid killing the app mid-write to prevent truncated snapshots","Keep regular datastore backups","Re-run checks after restoring or migrating data"],"tags":["flask","snapshot-corruption","http-500","decompression"],"backgroundTag":"corrupt-stored-data","analyzedSha":"5d9c7c6da76340597243e8163c4f2439237fa0e8","analyzedAt":"2026-08-27T19:41:16.067Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}