できない.dev

SQLite で「attempt to write a readonly database」が出て書き込めない

attempt to write a readonly database は、DB ファイルとそのディレクトリに書き込み権限を付ければ直る。
ジャーナルを作るためディレクトリにも権限が要る。
mode=ro や読み取り専用マウントでも出る。

公開: 更新:

症状できない · exit 1
su appuser -s /bin/sh -c "python insert.py /data/app.db"
attempt to write a readonly database
SQLITE_READONLY 8
解決後できた · exit 0
su appuser -s /bin/sh -c "python insert.py /data/app.db"
ok 1

2026-10-28 に python:3.12(Python 3.12.16 / Git 2.47.3) で実際に打って取った出力。検証環境の詳細

要約

SELECT は通るのに、INSERT や UPDATE を実行した時点で次のエラーになる。
Python の sqlite3 と、sqlite3 コマンドではそれぞれ次のように表示された(Python 3.13・SQLite 3.46.1)。

sqlite3.OperationalError: attempt to write a readonly database
Error: stepping, attempt to write a readonly database (8)

末尾の 8 は結果コード SQLITE_READONLY で、公式の説明(新しいタブで開く)では、接続が書き込み権限を持たないデータを変更しようとしたときに返る。
原因はファイルの権限とは限らない。
SQLite は書き込むたびに、データベースと同じディレクトリにジャーナルファイル(app.db-journal など)を作るので、ディレクトリに書き込めない場合も同じメッセージになる。

Python 3.11 以降なら、例外の sqlite_errorname で原因を切り分けられる。

import sqlite3
 
con = sqlite3.connect("/data/app.db")
try:
    con.execute("INSERT INTO t VALUES (1)")
    con.commit()
except sqlite3.OperationalError as e:
    print(e.sqlite_errorname, e.sqlite_errorcode)

ファイルに書き込めないときは SQLITE_READONLY 8、ファイルには書き込めてディレクトリに書き込めないときは SQLITE_READONLY_DIRECTORY 1544 が表示された。

実行例

python:3.12 のコンテナで root が作ったデータベースに一般ユーザーで INSERT すると、SQLITE_READONLY (8) で終了コード 1 になった。
ファイルを chmod 666 にしても、ディレクトリが root のままだと SQLITE_READONLY_DIRECTORY (1544) になった。

$ ls -l /data/app.db
-rw-r--r-- 1 root root 8192 Oct 28 01:08 /data/app.db
$ ls -ld /data
drwxr-xr-x 2 root root 4096 Oct 28 01:08 /data
$ su appuser -s /bin/sh -c "python insert.py /data/app.db"
attempt to write a readonly database
SQLITE_READONLY 8
$ echo $?
1
$ chmod 666 /data/app.db
$ su appuser -s /bin/sh -c "python insert.py /data/app.db"
attempt to write a readonly database
SQLITE_READONLY_DIRECTORY 1544
$ echo $?
1
$ chown -R appuser:appuser /data
$ ls -ld /data
drwxr-xr-x 2 appuser appuser 4096 Oct 28 01:08 /data
$ su appuser -s /bin/sh -c "python insert.py /data/app.db"
ok 1
$ echo $?
0
$ python insert.py "file:/data/app.db?mode=ro"
attempt to write a readonly database
SQLITE_READONLY 8
$ echo $?
1
$ python insert.py "file:/data/app.db?mode=rw"
ok 2
$ echo $?
0

— 2026-10-28 時点の出力

検証環境Python 3.12.16 / Git 2.47.3 / 2026-10-28 検証
検証日
実行環境
python:3.12 Debian GNU/Linux 13 (trixie)
バージョン
  • Python 3.12.16
  • Git 2.47.3

この記事の「実行例」は、上記の環境で実際にコマンドを実行して得られた出力をそのまま掲載しています。 再現手順はリポジトリの検証スクリプトとして管理し、定期的に再実行して出力を更新しています。

解決策

1. ファイルとディレクトリの両方を確かめる

ls -l /data/app.db
ls -ld /data

アプリを動かすユーザーが、ファイルとディレクトリの両方に書き込めるようにする。
データベースは専用のディレクトリにまとめ、ディレクトリごと所有者を変えるのが確実だ。

sudo chown -R appuser:appuser /data

ディレクトリにも書き込み権限を付けたところで、一般ユーザーからの INSERT が通った。
WAL モードでも、ジャーナルの代わりに -wal と -shm のファイルをデータベースと同じディレクトリに作る。

2. コンテナでは :ro を外し、所有者を合わせる

書き込むコンテナでは :ro を付けずにマウントする。
コンテナの中のアプリが root 以外のユーザーで動くなら、マウントするディレクトリの所有者もそのユーザーに合わせる。

3. 書き込む接続では mode=ro を外す

con = sqlite3.connect("file:/data/app.db?mode=rw", uri=True)

mode=rw はファイルが無いと unable to open database file で失敗し、mode=rwc は無ければ作る。
URI を使わない sqlite3.connect("/data/app.db") は rwc と同じ動きになる。

よくある原因

  1. ファイルの権限が足りない。
    root で作成したデータベースを、Web サーバーやコンテナの一般ユーザーで開いている。
  2. ディレクトリの権限が足りない。
    ファイルだけ chmod 666 にしても、ディレクトリに書き込めなければジャーナルファイルを作れない。
  3. 読み取り専用でマウントしている。docker run -v "$PWD/data:/data:ro" のように :ro を付けたマウントでは、ファイルの権限に関係なく書き込めない。
  4. mode=ro で開いている。sqlite3.connect("file:app.db?mode=ro", uri=True) のようなURI 指定(新しいタブで開く)で読み取り専用にしている。