Skip to content
SQLite

SQLite: attempt to write a readonly database

attempt to write a readonly database (the connection, the file or its folder is read-only)

Applies to: SQLite 3.x (examples run on 3.39.2; shell output from 3.50.6)
Last technically reviewed 24 October 2026 on SQLite 3.39.2 (PHP PDO_SQLITE) and the sqlite3 shell 3.50.6
The full message
attempt to write a readonly database
Other forms of this message
sqlite3 shell (3.50.6) opened with -readonly, running a script
Runtime error near line 2: attempt to write a readonly database (8)
sqlite3 shell (3.50.6), SQL given on the command line
Error: stepping, attempt to write a readonly database (8)
PHP PDO
SQLSTATE[HY000]: General error: 8 attempt to write a readonly database
Python sqlite3 (raised as sqlite3.OperationalError)
sqlite3.OperationalError: attempt to write a readonly database
Result code
SQLITE_READONLY (8); extended codes include SQLITE_READONLY_DIRECTORY (1544) and SQLITE_READONLY_DBMOVED (1032)
Product
SQLite library
Versions
SQLite 3.x (examples run on 3.39.2; shell output from 3.50.6)
Last reviewed
24 October 2026

What it means

This error means SQLite opened the database but is not allowed to change it, because the connection is read-only, the file or its folder is not writable, or writes were switched off.

SQLite raises SQLITE_READONLY when a statement tries to change a database that the connection may only read. The connection can be read-only on purpose (opened with SQLITE_OPEN_READONLY, a mode=ro URI, sqlite3 -readonly, or PRAGMA query_only = ON) or by accident: when SQLite cannot open the file for writing because of operating system permissions, it opens it read-only instead of failing, so the open succeeds and the error appears only at the first write. We saw this on Windows with a file that had the read-only attribute set.

Writing also needs the folder. In rollback-journal mode SQLite creates a -journal file next to the database for every write transaction, and in WAL mode it needs the -wal and -shm files, so a writable file in a folder the process cannot write to still fails (SQLITE_READONLY_DIRECTORY, 1544). That is the usual cause on Linux web servers, where the database is writable but its folder belongs to another user. If the file was moved or renamed while open, SQLite refuses further writes with SQLITE_READONLY_DBMOVED (1032). Most drivers show only 8; the extended codes need a driver that exposes them.

Common causes

  • The connection was opened read-only: a read-only flag in the driver, ?mode=ro in a URI filename, sqlite3 -readonly, or immutable=1.
  • PRAGMA query_only = ON was set on the connection, for example by a reporting tool or a framework's read replica setting.
  • The operating system does not let the process write the file: wrong owner (a web server user such as www-data cannot write a file created by your login), read-only file permissions or attribute, or read-only media such as an app bundle or a mounted image.
  • The folder that holds the database is not writable, so SQLite cannot create the journal, WAL or shared-memory files.
  • The database file was moved, replaced or deleted while a connection still had it open.

Diagnose it

Read-only These statements only read the schema (sqlite_schema and read-only PRAGMA queries) or your own data. They do not change data, settings or the database file, so they are safe to run first.

Is the connection in query-only mode?

PRAGMA query_only;
What to look for

1 means this connection refuses all changes, including CREATE TEMP TABLE. 0 means the restriction comes from how the file was opened or from the operating system.

Which file is the connection using?

PRAGMA database_list;
What to look for

The full path of the main database and any attached ones. Check the permissions of exactly this file and its folder; a relative path may point somewhere you did not expect.

Which journal files does a write need?

PRAGMA journal_mode;
What to look for

delete, truncate or persist: a write creates or uses shop.db-journal next to the file. wal: it needs shop.db-wal and shop.db-shm. The process must be able to create these in the folder.

Check file and folder permissions for the user that runs the code

On Linux or macOS, run ls -l /path/to/shop.db* and ls -ld /path/to and compare the owner and permissions with the user that runs your application (for a web server, often www-data or apache; check with ps). With SELinux, also check the security context with ls -Z. On Windows, check the file's Read-only attribute and the folder's security settings for that account.

How to fix it

Each fix is labelled with what it changes. Try read-only checks and the least invasive fix first, and test changes outside production.

Open the database read-write

Client or network side

Remove the read-only option where the connection is created: the read-only open flag in PHP (PDO::SQLITE_OPEN_READONLY or SQLITE3_OPEN_READONLY), mode=ro or immutable=1 in a URI filename (use mode=rw, or mode=rwc to allow creating it), or the -readonly option of the sqlite3 shell.

Turn query-only mode off for this connection

Changes configuration

If the connection was put into query-only mode deliberately (for a reporting user, for example), write through a different connection instead of switching it off.

PRAGMA query_only = OFF;

Give the application user write access to the file and its folder

Changes permissions

Make the user that runs the code the owner of the database file and of the folder that holds it, or put both in a group the application belongs to, for example sudo chown www-data:www-data /var/lib/myapp /var/lib/myapp/shop.db and chmod 770 on the folder, 660 on the file. Include any existing -wal, -shm or -journal files. Keep the database out of the public web root.

Move the database to a writable location

Client or network side

Files shipped inside an application (a program folder, an app bundle, Android assets, a read-only container image) are not writable at run time. On first start, copy the database to the platform's data folder (for example %LOCALAPPDATA% on Windows, the app's files directory on Android, a mounted volume in a container) and open the copy. For data that is only ever read from read-only media, open it with immutable=1 and convert it to rollback-journal mode before shipping, as the WAL documentation advises.

Examples

Writing through a connection opened read-only

-- shop.db already contains: CREATE TABLE orders (id INTEGER PRIMARY KEY, total REAL);
-- the connection is opened read-only (SQLITE_OPEN_READONLY, mode=ro or sqlite3 -readonly)

SELECT COUNT(*) FROM orders;              -- reading works
INSERT INTO orders (total) VALUES (25);
Result
attempt to write a readonly database

Reproduced on SQLite 3.39.2, 24 October 2026.

Writing after PRAGMA query_only = ON

CREATE TABLE orders (id INTEGER PRIMARY KEY, total REAL);

PRAGMA query_only = ON;
INSERT INTO orders (total) VALUES (25);
Result
attempt to write a readonly database

Reproduced on SQLite 3.39.2, 24 October 2026.

The folder that holds the database is not writable (Linux)

Shown as documented in the SQLite documentation; not reproduced here, because the lab runs on Windows, where Unix folder permissions cannot be set. The extended code is SQLITE_READONLY_DIRECTORY (1544), described in SQLite result and error codes.

-- shop.db is writable by the application user, but /var/lib/myapp is not
-- (for example owned by root with mode 755)

INSERT INTO orders (total) VALUES (25);  -- needs to create shop.db-journal in that folder
Result
attempt to write a readonly database

How to prevent it

Decide where the database lives at deployment time: a folder outside the web root that the application user owns, with the database and its journal files inside it. Create the file as that user (or fix ownership after copying it in), and run any migration or seeding scripts as the same user so they do not leave root-owned files behind.

Use read-only connections deliberately for reporting or untrusted queries, and give them a name in your code so a write path never picks one up by mistake. Do not copy, rename or replace a database file while connections are open; stop the application or use the backup API (VACUUM INTO or the sqlite3 shell's .backup) instead.

Common questions

How do I fix "attempt to write a readonly database" in SQLite?

Check PRAGMA query_only and how the connection was opened (a read-only flag or mode=ro). If neither applies, give the user that runs your code write permission on the database file and on the folder that contains it, because SQLite creates journal files there for every write.

The file is writable, so why does SQLite still say it is read-only?

Writes also need the folder: SQLite creates the -journal file (or the -wal and -shm files in WAL mode) next to the database. If the process cannot create files in that folder, the database is read-only for it. Give the application user write access to the folder too.

Why did opening the database succeed if I cannot write to it?

When SQLite cannot open a file for writing because of operating system permissions, it falls back to read-only instead of failing. Reads work, and the error appears at the first INSERT, UPDATE, DELETE or CREATE. If the file cannot be read at all, you get "unable to open database file" instead.

Learn more on CodeWithSQL

Official documentation

Last technically reviewed 24 October 2026; examples and diagnostics run on SQLite 3.39.2 (PHP PDO_SQLITE) and the sqlite3 shell 3.50.6. Messages are the text the SQLite library itself returns (sqlite3_errmsg) for the versions shown.

Still stuck on a SQLite error?

Browse the full SQLite error library, or get hands-on help from an experienced SQL professional.