SQLITE_READONLYERRORTier 1 — Safe✅ HIGH confidenceattempt to write a readonly database
What this means
SQLITE_READONLY (result code 8) is returned when SQLite attempts a write operation on a database that was opened in read-only mode, or whose underlying file has OS-level read-only permissions. The check is performed before any disk I/O so the database is never partially modified.
Why it happens
- 1The database file or its containing directory has read-only OS permissions (chmod 444)
- 2The connection was opened with the SQLITE_OPEN_READONLY flag
- 3The database is on a read-only filesystem (e.g., CD-ROM, read-only NFS mount)
- 4The WAL file or shared-memory file exists but is not writable while the database itself is readable
How to reproduce
A database file is made read-only at the OS level and then a write is attempted.
import sqlite3, os, stat
# Create and populate the database
conn = sqlite3.connect('/tmp/demo.db')
conn.execute('CREATE TABLE t (x INTEGER)')
conn.commit()
conn.close()
# Make it read-only
os.chmod('/tmp/demo.db', stat.S_IRUSR | stat.S_IRGRP)
# Attempt to write — triggers SQLITE_READONLY
conn = sqlite3.connect('/tmp/demo.db')
conn.execute('INSERT INTO t VALUES (1)')Fix 1: Restore write permissions
When the file should be writable but permissions were accidentally changed.
import os, stat
os.chmod('/tmp/demo.db', stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IWGRP)Why this works
SQLite checks the OS writability of the database file and its parent directory before opening a write transaction. Restoring write permission (mode 0644 or stricter) allows the library to acquire the write lock.
Fix 2: Open a copy of the database for writing
When the source database must remain read-only (e.g., shipped with an app).
import sqlite3, shutil
shutil.copy('/assets/readonly.db', '/data/user.db')
conn = sqlite3.connect('/data/user.db')
conn.execute('INSERT INTO t VALUES (1)')
conn.commit()Why this works
Copying to a writable location gives the application a mutable working copy while keeping the original read-only asset intact. Common pattern for mobile apps shipping a pre-populated SQLite database.
What not to do
Open the database with sqlite3.connect() and ignore the SQLITE_READONLY error
Why it's wrong: Silent swallowing of write errors means data changes are never persisted, leading to data loss and confusing application state.
Version notes
SQLite 3.8.0+SQLITE_READONLY_ROLLBACK (264) extended code added — emitted when a hot journal exists but the database is read-only, preventing rollback of an interrupted transaction.Dangerous variant
⚠️ Warning
SQLITE_READONLY_ROLLBACK (264) — a hot journal from a previously interrupted write exists but cannot be applied because the database is now read-only. The database may be in an inconsistent state until the journal is replayed.
Sources
📚 Official docs: https://www.sqlite.org/rescode.html#readonly
🔧 Source ref: sqlite3.h — SQLITE_READONLY = 8
📖 Further reading: SQLite URI filenames and open flags
Confidence assessment
✅ HIGH confidence
Stable and well-documented. Extended codes for READONLY variants are listed in sqlite3.h and the official result code page.
See also
🔗 Related errors
📄 Reference pages