How to fix SQLITE_READONLY_DBMOVED: Database file has been moved since opened in SQLite

SQLiteINTERMEDIATEHIGHUpdated Dec 28, 2025ยท 248 views

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
  1. Confirm the move actually happens while the database is open
  2. Always close every SQLite connection before moving or replacing the file
  3. Reopen the database from the new path instead of reusing a stale handle
  4. Use copy/backup techniques instead of moving a live file
sqlite โ€” error
PID=$(pgrep -f my-app)
lsof -p $PID | grep database.db
stat database.db
SQLITE_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

  1. An atomic-update pattern renames a temporary file over the original while the app still has an open SQLite connection
  2. Backup, sync, or uploader scripts move the database file without closing every SQLite handle first
  3. Hot reload or hot restart flows recreate the database file path while native bindings still hold a descriptor
  4. Third-party libraries rewrite or replace the database from another process and leave the original connection open
  5. Containers or VMs rename volumes or files during deployment, but the app keeps the old file descriptor alive
  6. 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"

1Confirm the move actually happens while the database is open

Use OS tools to inspect the open file path and detect renames. On Linux/macOS you can run:

bash
PID=$(pgrep -f my-app)
lsof -p $PID | grep database.db
stat database.db

Re-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.

2Always close every SQLite connection before moving or replacing the file

Wrap your rename/copy logic so every connection closes first. For example, with node-sqlite3:

javascript
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.

3Reopen the database from the new path instead of reusing a stale handle

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.

4Use copy/backup techniques instead of moving a live file

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.

5Treat the database file as immovable in clustered deployments

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