How to fix SQLITE_READONLY_DBMOVED: Database file has been moved since opened in SQLite
SQLite raises SQLITE_READONLY_DBMOVED when it detects that the database file has been moved or renamed after a connection was opened, so it refuses all writes to guard against rollback journal corruption.
Quick fix
Fixes SQLITE_READONLY_DBMOVED
PID=$(pgrep -f my-app)
lsof -p $PID | grep database.db
stat database.db- Confirm the move actually happens while the database is open
- Always close every SQLite connection before moving or replacing the file
- Reopen the database from the new path instead of reusing a stale handle
- Use copy/backup techniques instead of moving a live file
PID=$(pgrep -f my-app)lsof -p $PID | grep database.dbstat database.dbSQLITE_READONLY_DBMOVED: Database file has been moved since opened
What this error means
SQLITE_READONLY_DBMOVED is an extended SQLITE_READONLY status that fires when SQLite recognizes the database file was relocated after the handle was opened. Because the rollback journal lives next to the database file and uses the same basename, moving or renaming the file while a connection remains active could leave the journal pointing to a stale path when a crash occurs. SQLite responds by locking the database in read-only mode until every connection closes and reopens the file at the new location, ensuring the journal stays in sync with the database name.
Typical symptoms
- INSERT/UPDATE/DELETE statements fail with 'SQLITE_READONLY_DBMOVED: Database file has been moved since opened'
- Writes succeed until a deployment, hot reload, or migration renames or replaces the database file
- Reads continue to work because only write access is blocked
- The error follows synchronization or backup scripts that move the .db file while the app is running
- One process reports SQLITE_READONLY_DBMOVED while other processes can still read from the file
- Mobile hot restarts or desktop deployments that copy a new database into place trigger the error
Common causes
- An atomic-update pattern renames a temporary file over the original while the app still has an open SQLite connection
- Backup, sync, or uploader scripts move the database file without closing every SQLite handle first
- Hot reload or hot restart flows recreate the database file path while native bindings still hold a descriptor
- Third-party libraries rewrite or replace the database from another process and leave the original connection open
- Containers or VMs rename volumes or files during deployment, but the app keeps the old file descriptor alive
- File watchers (inotify, fswatch, etc.) rename or move the database before the server shuts down
How to fix "SQLITE_READONLY_DBMOVED: Database file has been moved since opened"
Use OS tools to inspect the open file path and detect renames. On Linux/macOS you can run:
PID=$(pgrep -f my-app)
lsof -p $PID | grep database.db
stat database.dbRe-run stat or ls immediately after your deployment script finishes and compare the inode. If the inode changes while the handle stays active, SQLite will classify the database as moved and block writes. On Windows, use Process Explorer or the Handle utility to inspect which process keeps the file open.
Wrap your rename/copy logic so every connection closes first. For example, with node-sqlite3:
await db.close();
fs.renameSync(tempPath, mainPath);
const reopened = new sqlite3.Database(mainPath, sqlite3.OPEN_READWRITE);Closing ensures SQLite knows the file move happened, so the new handle opens the database at the new path instead of seeing it as moved.
If an update must replace the file, discard the old Database object and create a new one after the rename. Always call sqlite3_close_v2 (or the async equivalent) on every connection before moving the file, then call sqlite3_open again once the file sits in its new place.
When you need to refresh data without stopping the app, keep the live file untouched and write to a temporary database instead:
1. Use sqlite3_backup_init/sqlite3_backup_step to copy data into a temporary file while the main connection stays open.
2. After the backup completes, close all connections, rename the temporary file into place, and restart your app.
3. Alternatively, run VACUUM INTO 'new.db' to produce a new copy without deleting the old one until you are ready to swap.
If multiple nodes share a mounted volume (NFS, SMB, etc.), rewrite the deployment so each node uses a local database or a client-server database. SQLite lacks multi-writer coordination, so moving the shared file while writers are active will always trigger SQLITE_READONLY_DBMOVED. Keep the file path stable and only replicate data via controlled snapshots or migrations.
Advanced notes
SQLite only raises SQLITE_READONLY_DBMOVED when it detects the on-disk path no longer points at the file that was originally opened. The rollback journal name is derived from that path, and if the file is moved the journal would go out of sync and risk corruption if a write is interrupted. Closing and reopening the connection tells SQLite about the new path so writability can resume.
Frequently asked questions
- What does "SQLITE_READONLY_DBMOVED" mean?
- SQLITE_READONLY_DBMOVED is an extended SQLITE_READONLY status that fires when SQLite recognizes the database file was relocated after the handle was opened. Because the rollback journal lives next to the database file and uses the same basename, moving or renaming the file while a connection remains active could leave the journal pointing to a stale path when a crash occurs. SQLite responds by locking the database in read-only mode until every connection closes and reopens the file at the new location, ensuring the journal stays in sync with the database name.
- How do I fix "SQLITE_READONLY_DBMOVED"?
- SQLite raises SQLITE_READONLY_DBMOVED when it detects that the database file has been moved or renamed after a connection was opened, so it refuses all writes to guard against rollback journal corruption.
- What causes "SQLITE_READONLY_DBMOVED"?
- An atomic-update pattern renames a temporary file over the original while the app still has an open SQLite connection Backup, sync, or uploader scripts move the database file without closing every SQLite handle first Hot reload or hot restart flows recreate the database file path while native bindings still hold a descriptor
- Is the "SQLITE_READONLY_DBMOVED" error serious?
- Yes. This is a high-severity error that usually blocks work until it is fixed, but the steps above resolve it.
Official resources & further reading
- ๐SQLite Official Documentation
- ๐Result and Error Codes โ SQLITE_READONLY_DBMOVED(Official Docs)
- ๐