dgtlmoon/changedetection.io · critical · Exception
Migration failed: Could not create changedetection.json. url
Error message
Migration failed: Could not create changedetection.json. url-watches.json remains intact, safe to retry. Error: {e} What it means
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.
Source
Thrown at changedetectionio/store/updates.py:631
if not os.path.isfile(watch_json):
missing.append(uuid)
if missing:
raise Exception(
f"Migration failed: {len(missing)} watch files missing: {missing[:5]}... "
f"url-watches.json remains intact, safe to retry."
)
logger.critical(f"Phase 2 complete: Verified {watch_count} watch files")
# Phase 3: Create new settings file
logger.critical("Phase 3/4: Creating changedetection.json...")
try:
self._save_settings()
except Exception as e:
logger.error(f"Failed to create changedetection.json: {e}")
raise Exception(
f"Migration failed: Could not create changedetection.json. "
f"url-watches.json remains intact, safe to retry. Error: {e}"
)
# Phase 4: Verify settings file exists
logger.critical("Phase 4/4: Verifying changedetection.json exists...")
changedetection_json_new_schema=os.path.join(self.datastore_path, "changedetection.json")
if not os.path.isfile(changedetection_json_new_schema):
import sys
logger.critical("Migration failed, changedetection.json not found after update ran!")
sys.exit(1)
logger.critical("Phase 4 complete: Verified changedetection.json exists")
# Success! Now reload from new format
logger.critical("Reloading datastore from new format...")
# write it to disk, it will be saved without ['watching'] in the JSON db because we find it from disk globView on GitHub (pinned to 5d9c7c6da7)
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
Defensive patterns
Strategy: retry
Validate before calling
import shutil # Phase 1 writes one dir per watch; ensure room before starting free = shutil.disk_usage(datastore_path).free assert free > 50 * 1024 * 1024, 'insufficient space for migration artifacts'
Try / catch
try:
update_26(datastore)
except Exception as e:
if 'Could not create changedetection.json' in str(e):
free_space(); restart_app() # retried safely, url-watches.json preserved Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Disk full: Cannot save {label}
- Migration failed: Could not save watch {uuid}. url-watches.j
- Migration failed: {len(missing)} watch files missing: {missi
- Disk quota exceeded: Cannot save {label}
- I/O error saving {label}: {e}
AI-assisted analysis of dgtlmoon/changedetection.io@5d9c7c6da7 (2026-08-27).
Data as JSON: /api/errors/325293f6ee833d7b.
Report an issue: GitHub.