{"record":{"id":"60ddae6d25e00e17","repo":"dgtlmoon/changedetection.io","slug":"migration-failed-could-not-save-watch-uuid-url","errorCode":null,"errorMessage":"Migration failed: Could not save watch {uuid}. url-watches.json remains intact, safe to retry. Error: {e}","messagePattern":"Migration failed: Could not save watch (.+?)\\. url-watches\\.json remains intact, safe to retry\\. Error: (.+?)","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"critical","filePath":"changedetectionio/store/updates.py","lineNumber":600,"sourceCode":"        logger.info(\"Populating settings from legacy data...\")\n        watch_count = len(self.data['watching'])\n        logger.success(f\"Loaded {watch_count} watches from legacy format\")\n\n        # Phase 1: Save all watches to individual files\n        logger.critical(f\"Phase 1/4: Saving {watch_count} watches to individual watch.json files...\")\n\n        saved_count = 0\n        for uuid, watch in self.data['watching'].items():\n            try:\n                watch.commit()\n                saved_count += 1\n\n                if saved_count % 100 == 0:\n                    logger.info(f\"  Progress: {saved_count}/{watch_count} watches migrated...\")\n\n            except Exception as e:\n                logger.error(f\"Failed to save watch {uuid}: {e}\")\n                raise Exception(\n                    f\"Migration failed: Could not save watch {uuid}. \"\n                    f\"url-watches.json remains intact, safe to retry. Error: {e}\"\n                )\n\n        logger.critical(f\"Phase 1 complete: Saved {saved_count} watches\")\n\n        # Phase 2: Verify all files exist\n        logger.critical(\"Phase 2/4: Verifying all watch.json files were created...\")\n\n        missing = []\n        for uuid in self.data['watching'].keys():\n            watch_json = os.path.join(self.datastore_path, uuid, \"watch.json\")\n            if not os.path.isfile(watch_json):\n                missing.append(uuid)\n\n        if missing:\n            raise Exception(\n                f\"Migration failed: {len(missing)} watch files missing: {missing[:5]}... \"","sourceCodeStart":582,"sourceCodeEnd":618,"githubUrl":"https://github.com/dgtlmoon/changedetection.io/blob/5d9c7c6da76340597243e8163c4f2439237fa0e8/changedetectionio/store/updates.py#L582-L618","documentation":"A generic Exception raised inside update_26's migrate_legacy_db_format (Phase 1) when saving a single migrated watch to <uuid>/watch.json raises. The migration wraps each watch save so one bad record aborts with context; url-watches.json is deliberately left untouched so the migration can be retried safely.","triggerScenarios":"Running the v26 datastore migration on a legacy url-watches.json where one watch entry contains data that cannot be serialized/saved (corrupt entry, unserializable value, permission/disk error on that subdirectory). The inner save's exception text is appended after 'Error: '.","commonSituations":"Upgrading an old changedetection.io instance with hand-edited or partially corrupt url-watches.json; datastore dir with mixed ownership; disk filling mid-migration.","solutions":["Read the appended inner error '{e}' — it identifies the actual save failure for that uuid","Fix the specific watch entry in url-watches.json (remove/repair the offending uuid) and restart to retry","Check disk space and directory permissions on the datastore path","Back up url-watches.json before retrying — it is your recovery point, keep it intact"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"# before upgrading: back up and sanity-parse the legacy file\nimport json\ndata = json.load(open('url-watches.json'))\nassert all(isinstance(v, dict) for v in data.get('watching', {}).values())","typeGuard":null,"tryCatchPattern":"try:\n    update_26(datastore)\nexcept Exception as e:\n    if 'Migration failed' in str(e):\n        # url-watches.json is intact; fix cause (disk/perms/bad entry) and restart to retry\n        backup_datastore(); investigate(str(e))","preventionTips":["Always back up url-watches.json and the datastore before version upgrades","Ensure ample disk space before migrating large watch collections","Never run two versions against one datastore during upgrade windows"],"tags":["migration","datastore","upgrade","legacy-format"],"backgroundTag":"database-migration-failed","analyzedSha":"5d9c7c6da76340597243e8163c4f2439237fa0e8","analyzedAt":"2026-08-27T19:41:16.067Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}