Summary
opencode Desktop opens multiple independent SQLite connections to the same opencode.db (one per TUI backend + one from the Electron controller). Because each connection holds a long-lived read transaction, the WAL can never be checkpointed past the oldest reader. wal_autocheckpoint silently fails, PRAGMA wal_checkpoint(TRUNCATE) returns 1 (BUSY), and the WAL grows without bound during normal use until the disk is full. Closing the Desktop kills all connections → checkpoint finally runs → space returns.
This matches the symptom users are reporting: "opencode eats all available disk space while running, even when idle. After quitting, free space comes back."
Environment
- opencode Desktop: v1.18.1 (Linux build,
/opt/OpenCode/ai.opencode.desktop)
- OS: Linux x86_64, ext4 on NVMe
- Window manager: multiple windows/tabs of opencode Desktop open simultaneously
- DB:
~/.local/share/opencode/opencode.db (SQLite in WAL mode)
PRAGMA wal_autocheckpoint: 1000 (default, correctly set — but ineffective, see below)
Reproduction
- Launch opencode Desktop.
- Open 2+ windows (or wait — even a single Desktop launch spawns 3 TUI backends + 1 Electron utility in my case).
- Use the app normally for ~30–60 min (any session activity: streaming tokens, tool calls, MCP polling).
- Observe
opencode.db-wal growing continuously; df shows free space dropping.
- After hours/days of uptime with heavy MCP/plugin usage, the disk reaches 0 free.
- Quit opencode Desktop completely → free space returns (WAL truncates on connection close).
Observed on my machine (single Desktop launch)
$ lsof ~/.local/share/opencode/opencode.db
COMMAND PID NAME
opencode 1798003 ~/.local/share/opencode/opencode.db ← TUI backend
opencode 1837890 ~/.local/share/opencode/opencode.db ← TUI backend
opencode 1842501 ~/.local/share/opencode/opencode.db ← TUI backend (RSS 3.3 GB)
ai.opencode.desktop 1855509 .../opencode.db ← Electron utility
4 concurrent connections to the same database file, each holding an open read transaction.
Root cause
SQLite's WAL mode has a fundamental constraint:
A read transaction prevents the WAL from being checkpointed past the point at which the read transaction was opened. Checkpoint can only reclaim frames older than the oldest active reader.
With N ≥ 2 always-open connections that never release their read snapshots simultaneously:
- Every token-stream chunk, tool output, and status update writes a new frame to the WAL (event-sourcing pattern:
message.part.updated, message.updated, session.updated events dominate — I have 28,881 / 12,059 / 3,668 historical rows respectively).
wal_autocheckpoint=1000 tries to fire every 1000 pages → blocked by concurrent readers → silently no-ops.
- WAL grows linearly with session activity.
- On Desktop quit: all 4 processes die, all connections drop, SQLite's shutdown hook runs
wal_checkpoint(TRUNCATE) → WAL merges into main db + truncates to ~0 → space returns.
Direct evidence — checkpoint returns BUSY
$ sqlite3 ~/.local/share/opencode/opencode.db 'PRAGMA wal_checkpoint(TRUNCATE);'
1|950|930
↑
1 = SQLITE_BUSY (checkpoint could not complete because of concurrent readers)
$ du -h ~/.local/share/opencode/opencode.db-wal
2,4G ~/.local/share/opencode/opencode.db-wal ← unchanged after the "TRUNCATE"
WAL growth observed
| Time |
WAL size |
Δ |
| 16:09 |
462 MB |
— |
| 16:30 |
2.4 GB |
+1.9 GB / 21 min |
Extrapolating linearly to a multi-day session with heavy MCP usage → 10–15 GB. Matches the user-reported symptom.
Expected behavior
- WAL should be bounded (a few hundred MB at most) thanks to
wal_autocheckpoint.
PRAGMA wal_checkpoint(TRUNCATE) issued from any external tool should succeed and reclaim the space.
- The Desktop should be able to run for days without filling the disk.
Actual behavior
- WAL grows unbounded during runtime.
- Auto-checkpoint is ineffective.
- Manual
TRUNCATE returns BUSY while the app is running.
- Disk fills to 0; only a full Desktop quit reclaims the space.
Suggested fixes (any one of these would resolve it)
- Pool DB access through a single process (Desktop controller owns the only write connection; backends talk to it via IPC). This is the correct long-term fix and matches how Tauri/Electron apps typically handle SQLite.
- Use
PRAGMA wal_checkpoint(RESTART) on a timer from the primary writer, after ensuring no long-lived read transactions are held (open read cursors with BEGIN DEFERRED and commit promptly).
- Switch to
journal_mode=DELETE (regression in write throughput, but bounds disk usage to the main db file).
- At minimum, expose a "compact now" action in the Desktop UI that closes & reopens all connections, runs
wal_checkpoint(TRUNCATE), and reports reclaimed space.
- Investigate why 3 TUI backends are spawned per Desktop launch — appears to be a separate bug (window/tab lifecycle leak).
Workarounds for affected users
- Keep only one opencode Desktop window open. Each additional window multiplies the contention.
- Periodically quit & relaunch Desktop after long sessions (frees the WAL).
- External checkpoint won't work while the app is running (BUSY), so don't bother scripting it.
- Delete stale
.bak files that accumulate independently (not a runtime issue, but worth cleaning):
ls -lah ~/.local/share/opencode/opencode.db.bak.*
# rm after verification
Diagnostic script
Anyone wanting to verify on their machine:
echo "=== concurrent DB connections ==="
lsof ~/.local/share/opencode/opencode.db | tail -n +2 | awk '{print $1, "pid="$2}' | sort -u
echo "=== WAL size ==="
du -h ~/.local/share/opencode/opencode.db-wal
echo "=== try checkpoint (1 = BUSY = bug present) ==="
sqlite3 ~/.local/share/opencode/opencode.db 'PRAGMA wal_checkpoint(TRUNCATE);'
echo "=== autocheckpoint setting ==="
sqlite3 ~/.local/share/opencode/opencode.db 'PRAGMA wal_autocheckpoint;'
If the checkpoint returns 1|...|..., the bug is reproducing on your machine too.
Related
Priority: High — silently destroys disk capacity for any user with long sessions and plugins.
/cc @thdxr
Plugins
No response
OpenCode version
1.18.3
Steps to reproduce
- Launch opencode Desktop.
- Open 2+ windows (or wait — even a single Desktop launch spawns 3 TUI backends + 1 Electron utility in my case).
- Use the app normally for ~30–60 min (any session activity: streaming tokens, tool calls, MCP polling).
- Observe
opencode.db-wal growing continuously; df shows free space dropping.
- After hours/days of uptime with heavy MCP/plugin usage, the disk reaches 0 free.
- Quit opencode Desktop completely → free space returns (WAL truncates on connection close).
Screenshot and/or share link
No response
Operating System
Manjaro Linux
Terminal
No response
Summary
opencode Desktop opens multiple independent SQLite connections to the same
opencode.db(one per TUI backend + one from the Electron controller). Because each connection holds a long-lived read transaction, the WAL can never be checkpointed past the oldest reader.wal_autocheckpointsilently fails,PRAGMA wal_checkpoint(TRUNCATE)returns1(BUSY), and the WAL grows without bound during normal use until the disk is full. Closing the Desktop kills all connections → checkpoint finally runs → space returns.This matches the symptom users are reporting: "opencode eats all available disk space while running, even when idle. After quitting, free space comes back."
Environment
/opt/OpenCode/ai.opencode.desktop)~/.local/share/opencode/opencode.db(SQLite in WAL mode)PRAGMA wal_autocheckpoint:1000(default, correctly set — but ineffective, see below)Reproduction
opencode.db-walgrowing continuously;dfshows free space dropping.Observed on my machine (single Desktop launch)
4 concurrent connections to the same database file, each holding an open read transaction.
Root cause
SQLite's WAL mode has a fundamental constraint:
With N ≥ 2 always-open connections that never release their read snapshots simultaneously:
message.part.updated,message.updated,session.updatedevents dominate — I have 28,881 / 12,059 / 3,668 historical rows respectively).wal_autocheckpoint=1000tries to fire every 1000 pages → blocked by concurrent readers → silently no-ops.wal_checkpoint(TRUNCATE)→ WAL merges into main db + truncates to ~0 → space returns.Direct evidence — checkpoint returns BUSY
WAL growth observed
Extrapolating linearly to a multi-day session with heavy MCP usage → 10–15 GB. Matches the user-reported symptom.
Expected behavior
wal_autocheckpoint.PRAGMA wal_checkpoint(TRUNCATE)issued from any external tool should succeed and reclaim the space.Actual behavior
TRUNCATEreturns BUSY while the app is running.Suggested fixes (any one of these would resolve it)
PRAGMA wal_checkpoint(RESTART)on a timer from the primary writer, after ensuring no long-lived read transactions are held (open read cursors withBEGIN DEFERREDand commit promptly).journal_mode=DELETE(regression in write throughput, but bounds disk usage to the main db file).wal_checkpoint(TRUNCATE), and reports reclaimed space.Workarounds for affected users
.bakfiles that accumulate independently (not a runtime issue, but worth cleaning):Diagnostic script
Anyone wanting to verify on their machine:
If the checkpoint returns
1|...|..., the bug is reproducing on your machine too.Related
Priority: High — silently destroys disk capacity for any user with long sessions and plugins.
/cc @thdxr
Plugins
No response
OpenCode version
1.18.3
Steps to reproduce
opencode.db-walgrowing continuously;dfshows free space dropping.Screenshot and/or share link
No response
Operating System
Manjaro Linux
Terminal
No response