attempt to write a readonly database (hot journal present)
Production Risk
SQLITE_READONLY_ROLLBACK in production means the database pages are in an indeterminate state. Do not serve data from the database until the journal has been properly rolled back via a writable connection.
SQLITE_READONLY_ROLLBACK (extended code 264) occurs when SQLite opens a read-only database file but finds a hot journal (an unfinished rollback journal from a previously interrupted write transaction). To recover, SQLite must replay the journal to roll back the incomplete transaction — but because the database is read-only it cannot do so, leaving the database in an uncertain state.
- 1A write transaction was interrupted (crash, power loss) leaving a -journal file
- 2The database and journal files were then moved to a read-only location
- 3The application opened the file with SQLITE_OPEN_READONLY but the journal exists
A journal file exists from a previous crash but the database is now read-only.
# 1. Create a database and simulate a crash (leave the journal)
import sqlite3, os
conn = sqlite3.connect('demo.db')
conn.execute('CREATE TABLE t (x INTEGER)')
# Simulate crash: kill the process before commit leaves demo.db-journal
# 2. Make the file read-only
os.chmod('demo.db', 0o444)
# 3. Try to open read-only — triggers SQLITE_READONLY_ROLLBACK
conn2 = sqlite3.connect('demo.db')
conn2.execute('SELECT * FROM t')expected output
sqlite3.OperationalError: attempt to write a readonly database
Fix
Restore write permissions, recover, then re-apply read-only
WHEN When the database must be recovered before being served as read-only.
import sqlite3, os, stat
# Temporarily restore write permission
os.chmod('demo.db', stat.S_IRUSR | stat.S_IWUSR)
# Open as writable — SQLite will automatically replay the journal
conn = sqlite3.connect('demo.db')
conn.execute('SELECT * FROM t') # journal rolled back transparently
conn.close()
# Re-apply read-only permission
os.chmod('demo.db', stat.S_IRUSR)Why this works
When SQLite opens a writable connection and finds a hot journal, it automatically replays the journal to roll back the incomplete transaction. After this the database is consistent. The file can then be made read-only again.
✕ Delete the journal file manually to get past the error
Deleting the journal without replaying it leaves the database pages in a partially-written state from the interrupted transaction. The database will appear to open successfully but contain corrupted or incomplete data.
Extended code 264 introduced to distinguish this specific readonly scenario.
sqlite3.h — SQLITE_READONLY_ROLLBACK = 264
SQLite how to corrupt a database file ↗Content generated with AI assistance and reviewed for accuracy. Found an error? hello@errcodes.dev