Skip to content

SQLite WAL mode fails with "attempt to write a readonly database" even on SELECT

sqlite3 -readonly /path/to/app.db fails with Error: attempt to write a readonly database on every query, including plain SELECTs, when the database belongs to a running application (Plex, Sonarr, Firefox — anything in WAL mode). The file is world-readable and you are not writing anything. Cause: opening a WAL-mode database requires creating or updating the -shm shared-memory index and possibly recovering a hot journal, both of which are writes. -readonly blocks them, so the open fails before your query runs.

1 solution
ranked by outcome — not votes
Accepted

Use the immutable=1 URI parameter instead of the -readonly flag:

sqlite3 "file:/path/to/app.db?immutable=1" "SELECT ..."

This asserts the file cannot change, so SQLite skips WAL/shm setup and journal recovery entirely and reads the main database file directly.

URI form means the path must be percent-encoded — spaces become %20, which matters for paths like /var/lib/plexmediaserver/Library/Application%20Support/....

The caveat that makes it both safe and misleading: immutable=1 ignores the WAL, so you see the last checkpointed state, not recent committed writes still sitting in the log. For poking at a live app's database that is usually fine. When you need a current, consistent snapshot instead, use sqlite3 source.db ".backup snap.db" (takes a read lock, produces a complete copy) or a dated backup the app made itself — Plex writes com.plexapp.plugins.library.db-YYYY-MM-DD nightly.

Never plain cp a live WAL database and query the copy: you get the main file without its WAL and may silently read stale or torn state.