{"record":{"id":"325293f6ee833d7b","repo":"dgtlmoon/changedetection.io","slug":"migration-failed-could-not-create-changedetection","errorCode":null,"errorMessage":"Migration failed: Could not create changedetection.json. url-watches.json remains intact, safe to retry. Error: {e}","messagePattern":"Migration failed: Could not create changedetection\\.json\\. url-watches\\.json remains intact, safe to retry\\. Error: (.+?)","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"critical","filePath":"changedetectionio/store/updates.py","lineNumber":631,"sourceCode":"            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]}... \"\n                f\"url-watches.json remains intact, safe to retry.\"\n            )\n\n        logger.critical(f\"Phase 2 complete: Verified {watch_count} watch files\")\n\n        # Phase 3: Create new settings file\n        logger.critical(\"Phase 3/4: Creating changedetection.json...\")\n\n        try:\n            self._save_settings()\n        except Exception as e:\n            logger.error(f\"Failed to create changedetection.json: {e}\")\n            raise Exception(\n                f\"Migration failed: Could not create changedetection.json. \"\n                f\"url-watches.json remains intact, safe to retry. Error: {e}\"\n            )\n\n        # Phase 4: Verify settings file exists\n        logger.critical(\"Phase 4/4: Verifying changedetection.json exists...\")\n        changedetection_json_new_schema=os.path.join(self.datastore_path, \"changedetection.json\")\n        if not os.path.isfile(changedetection_json_new_schema):\n            import sys\n            logger.critical(\"Migration failed, changedetection.json not found after update ran!\")\n            sys.exit(1)\n\n\n        logger.critical(\"Phase 4 complete: Verified changedetection.json exists\")\n\n        # Success! Now reload from new format\n        logger.critical(\"Reloading datastore from new format...\")\n        # write it to disk, it will be saved without ['watching'] in the JSON db because we find it from disk glob","sourceCodeStart":613,"sourceCodeEnd":649,"githubUrl":"https://github.com/dgtlmoon/changedetection.io/blob/5d9c7c6da76340597243e8163c4f2439237fa0e8/changedetectionio/store/updates.py#L613-L649","documentation":"A generic Exception raised in Phase 3 of migrate_legacy_db_format when self._save_settings() fails while writing the new changedetection.json. This is the final artifact of the legacy migration; on failure the old url-watches.json is preserved so nothing is lost and the migration can be retried.","triggerScenarios":"The settings save hitting an I/O problem — disk full (ENOSPC surfaces as OSError from save_json_atomic), read-only filesystem, or permission loss on the datastore root — exactly when the migration tries to materialize changedetection.json.","commonSituations":"Disk exhausted by the thousands of watch.json files written in Phase 1 (the most common cause); datastore mounted read-only; ownership mismatch after container user change.","solutions":["Check free space on the datastore volume and free some if full (Phase 1 writes can consume a lot)","Verify the datastore dir is writable by the app user and not mounted read-only","Fix the underlying save error shown after 'Error: ', then restart the app to retry the migration","Keep the url-watches.json backup until the app boots cleanly with changedetection.json present"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"import shutil\n# Phase 1 writes one dir per watch; ensure room before starting\nfree = shutil.disk_usage(datastore_path).free\nassert free > 50 * 1024 * 1024, 'insufficient space for migration artifacts'","typeGuard":null,"tryCatchPattern":"try:\n    update_26(datastore)\nexcept Exception as e:\n    if 'Could not create changedetection.json' in str(e):\n        free_space(); restart_app()  # retried safely, url-watches.json preserved","preventionTips":["Free generous disk space before upgrading (Phase 1 multiplies file count)","Verify write permissions on the datastore root for the app user","Confirm changedetection.json exists and app boots before deleting any backup"],"tags":["migration","settings","disk-full","datastore"],"backgroundTag":"database-migration-failed","analyzedSha":"5d9c7c6da76340597243e8163c4f2439237fa0e8","analyzedAt":"2026-08-27T19:41:16.067Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}