Repository navigation
Closed as not planned
Closed as not planned
Copy link
Description
Bug Report
OpenCode version: latest (as of 2026-02-24)
OS: Linux 6.6.105+ (NFS-mounted home directory)
Runtime: Bun
Description
Opening multiple opencode sessions in the same repository corrupts the shared SQLite database (~/.local/share/opencode/opencode.db), making opencode unable to start until the database is manually deleted.
Error
{
"name": "UnknownError",
"data": {
"message": "SQLiteError: database disk image is malformed\n at values (unknown)\n at get (../../node_modules/.bun/[email protected]+1f516382cd87cd66/node_modules/drizzle-orm/bun-sqlite/session.js:91:25)\n at run (node:async_hooks:62:22)\n at use (src/storage/db.ts:111:28)\n at fromDirectory (src/project/project.ts:193:26)\n at async <anonymous> (src/project/instance.ts:27:52)\n at async provide (src/project/instance.ts:40:23)\n at processTicksAndRejections (native:7:39)"
}
}
Steps to Reproduce
- Have opencode installed with home directory on NFS
- Open an opencode session in a repository
- Open a second opencode session in the same (or different) repository
- Send a message in either session
- SQLite database corrupts almost immediately — opencode crashes with the above error
- All subsequent attempts to start opencode also crash until the database is manually deleted
Root Cause (Suspected)
All opencode sessions share a single SQLite database (~/.local/share/opencode/opencode.db) using WAL mode. Concurrent writers likely cause corruption due to:
- NFS + SQLite locking: SQLite relies on POSIX
fcntl()advisory locking, which is notoriously unreliable on NFS. The WAL-shmfile uses shared memory mappings that don't work correctly over network filesystems. - bun-sqlite concurrency: Even on local filesystems, multiple processes writing to the same bun-sqlite database without explicit coordination (e.g.,
PRAGMA busy_timeout, retry logic, or a single-writer architecture) can corrupt the database.
Evidence of NFS involvement: stale .nfs* handle files were present alongside the corrupted database in ~/.local/share/opencode/.
Workaround
Delete the corrupted database and let opencode recreate it (loses all session history):
rm ~/.local/share/opencode/opencode.db ~/.local/share/opencode/opencode.db-shm ~/.local/share/opencode/opencode.db-walSuggested Fixes
- Set
PRAGMA busy_timeoutto handle concurrent access gracefully instead of corrupting - Use
PRAGMA journal_mode=DELETEinstead of WAL when on a network filesystem (or detect NFS and warn) - Consider per-project databases instead of a single global one
- Add integrity checks on startup with a graceful recovery path instead of crashing
Related Issues
- Concurrent sessions working on different repos interfer each other #4251 — Concurrent sessions interfere with each other (session isolation)
- Sessions are not saving with multi sessions on #5241 — Sessions not saving with multiple sessions open
- OpenCode API Race Condition in Concurrent Session Deletion #5517 — Race condition in concurrent session deletion
Reacted by Dawid Krasowski, Ian Schmitz, Tom, Harsh Hemani, Daniel Gak Anagrov, Art Shendrik, Zachary Cutlip, sunbinbin007, Miguel Rueda, BrentRodhouse and 15 more
Metadata
Metadata
Assignees
Labels
No labels
Activity
jerry-xu0514 commentedon Feb 25, 2026
Follow-up Investigation (2026-02-25) — Third Corruption Incident
The corruption has occurred again today (Feb 25), the third time in one week. Sessions were running in separate repositories this time. Posting detailed findings with clear separation of observed facts vs. inferences.
Observed Facts
1. The database is on NFS — confirmed
NFS mount options:
vers=3, local_lock=none, proto=tcp, nconnect=7, hard2. The current database is corrupted right now
Database stats: 122 pages × 4096 bytes = 500KB main db, WAL = 2.7MB (656 frames), SHM = 32KB.
The database was recently recreated after the previous corruption (only 2 sessions, 18 messages, 71 parts exist) — meaning it corrupted again almost immediately after recovery.
3. Six stale
.nfs*handle files — physical evidence of the corruption mechanismThese
.nfs*files are NFS "silly rename" artifacts. They are created when a file is deleted (rm) while another process still has it open. NFS cannot truly delete the file, so it renames it to.nfsXXXXand deletes it when the last file handle closes.This means: when the corrupted database was deleted to recover, at least one other opencode process still had the database file open. This confirms concurrent multi-process access was occurring.
Note the database sizes grew dramatically between incidents: 45MB → 191MB, which is consistent with unchecked WAL growth from uncoordinated concurrent writers.
4. Installed opencode v1.2.14 has no NFS protection
The installed binary does not contain any NFS detection, integrity check, or fallback logic.
5.
PRAGMA busy_timeoutIS set in the source codeThe original issue suggested adding
busy_timeout— it's actually already there (PRAGMA busy_timeout = 5000). This pragma is not sufficient to prevent corruption on NFS because the problem is not lock contention timeout but rather incoherent shared memory (the-shmmmap).busy_timeoutonly helps when locks work correctly but are held by another process; on NFS, the locks themselves are unreliable.6. WAL checkpoint behavior reveals the corruption mechanism
On first connection via python3, integrity check failed (errors above). On second connection moments later, integrity check passed and all tables were readable. This is consistent with WAL-index (
-shm) corruption rather than main database file corruption: the second connection rebuilt the WAL index from scratch, recovering the data. This is exactly the failure mode described in SQLite's documentation on WAL mode over network filesystems — the-shmshared memory mappings become incoherent across NFS clients.Inferences (not directly observed, but strongly supported)
The corruption is caused by WAL mode on NFS, not by a concurrency bug in opencode's code. The
.nfs*stale handles, the-shmfile corruption pattern, and the NFS mount type all point to this. SQLite's own documentation explicitly warns against this configuration.Sessions on different repos still collide because all projects share one global database file (
~/.local/share/opencode/opencode.db). Even if the sessions are logically independent, they write to the same SQLite file and contend for the same WAL/SHM.The corruption can happen with a single session on NFS if the process crashes or is killed without cleanly closing the database, leaving the WAL in an inconsistent state that the next process cannot recover from over NFS. (Though multi-session definitely makes it more likely.)
Assessment of Proposed Fixes
We've prototyped fixes locally (not yet released). Here's what they do and whether they'd work:
Fix 1: NFS detection +
/tmpfallbackWould this fix the corruption? Yes. We confirmed that
statfsSync().typereturns0x6969on this system, so the detection works correctly. Moving the database to/tmpavoids NFS entirely, and WAL mode works perfectly on local filesystems.Trade-off:
/tmpis ephemeral — session history is lost on machine reboot. For our use case (ephemeral dev containers), this is acceptable. For persistent workstations with NFS home dirs, a configurable local path (e.g.,OPENCODE_DATA_DIRenv var) would be better.Fix 2: Integrity check + auto-recreation on startup
Would this prevent the hard crash? Yes. Instead of crashing with "database disk image is malformed", opencode would detect the corruption, log a warning, delete the database, and start fresh. This is defense-in-depth — it doesn't prevent corruption but makes it self-healing.
Would this prevent data loss? No. The database is still recreated from scratch; all session history is lost. But at least the user doesn't have to manually intervene.
Fix 3 (not implemented):
PRAGMA journal_mode = DELETEon NFSAn alternative to the
/tmpfallback. DELETE mode doesn't use shared memory mappings, so it's more NFS-compatible. However, it's slower for concurrent reads and still relies on NFS advisory locks, which are fragile. We chose the/tmpapproach as more robust.Recommendation
Ship fixes 1 + 2 together:
OPENCODE_DB_PATHorOPENCODE_DATA_DIRenv var override for users who want control over where the database livesTimeline
fix(core): detect NFS and use local database path to prevent corruption
Dulani commentedon Feb 27, 2026
We’re seeing a very similar failure mode, but triggered by multi-server access to a shared NFS home (not just multiple sessions on one host).
Setup:
Why this matters:
Workaround that solved it for us:
export XDG_DATA_HOME="/home/RAND.ORG/dwoods/.local/share/opencode-vincent"
Miskamyasa commentedon Mar 11, 2026
About workaround. It means, you need to auth each instance separatly or provide keys to each container. There is no way to separate auth.json from data folder as I can see from the code.
zcutlip commentedon Mar 23, 2026
I'm using opencode in a sandboxed container, and this happened. I asked opencode running from my host to try and repair the container instance. May not work every time, but it worked in this case, so I had opencode write a skill for what it did. Attached in case it's useful. Adapt paths accordingly.
Prerequisite: you need a functioning opencode instance independent of the instance with the corrupted database.
etherops commentedon Mar 25, 2026
Ran into the same issue running like this:
docker run -ti --rm -v ~/.local/share/opencode:/root/.local/share/opencode opencode-devAttempted out of the box sqllite
.recoverwhich restored session and part tables but the message table pages were on the corrupted blocks and came back empty - leaving me with historical session but ZERO conversation history! no good.Was able to start a new opencode database and Opus 4.6 figured out how to actually surigicall reconstruct messages from the parts table. Here is the script it came up with in case someone else needs this workaround. good luck we're all counting on you.
https://gist.github.com/etherops/df78300a0ce6eeb08e056533bdfa30d9
My suggestions if you run into this and
.recoverisn't good enough, blow away and get a fresh opencode database and give it that python code as guidance instead of trying to run this on your system which will break obv due to hard coded filenames etc.Notes:
mattjohnsonpint commentedon May 5, 2026
I hit this multiple times a week using a Colima-based devcontainer (ie., virtio not NFS). Alternative is to use a docker volume to keep the db on the VM instead of shared with the host - but sharing with the host is very useful, so I hope this gets fixed soon.
spoletumpr commentedon May 12, 2026
Same problem here using a devcontainer.
Sleepful commentedon May 28, 2026
Same issue running inside docker with
which is really cool so it is a shame it fails : (
Running opencode from source and using @zcutlip guideline was a quick fix.
zcutlip commentedon May 28, 2026
I'm curious if everyone using this is on macOS as a host. Whether via Docker desktop, Colima, Apple Containers, or some other virtualization/containerization stack.
I believe there may be a bug in the Apple Virtualization Framework that results in data inconsistency/corruption when guest and host have a shared folder.
Most interesting is if anyone experiencing this is not using macOS at all.
GaryGong27 commentedon Jul 10, 2026
Seeing the same
Project.fromDirectory/db.tscorruption path on 1.17.15–1.17.18 (macOS + Linux, local disk — not only NFS), with an additional trigger worth noting: cross-version schema drift on the sharedopencode.db.Because the DB is shared by every opencode binary on the machine, if one version migrates the schema forward (drops/renames a column) and an older version then queries the old column at boot, you get:
→ the server worker crash-loops and the interactive TUI renders a blank screen with no visible error (the error is only in
~/.local/share/opencode/log).Two asks:
oldantique commentedon Jul 11, 2026
Workaround that's been solid for me: give each process its own data dir via
XDG_DATA_HOMEI was hitting the locking/corruption intermittently when fanning out multiple one-shot
opencode runcalls in parallel. Root cause is the same one discussed here: every process on the machine shares the single global DB at~/.local/share/opencode/opencode.db.Since the config (
~/.config/opencode) and the models cache (~/.cache/opencode) resolve independently of the data dir, you can isolate just the SQLite side per process:Wrapped, preserving the exit code:
Why it works / what it costs:
auth.jsonlives in the data dir, hence thecpabove (re: @Miskamyasa's point). If your providers use env-var keys ({env:...}inopencode.json), you can drop that line;$TMPDIRis a local disk, and the fresh per-run DB can't hit the cross-version schema-drift case @GaryGong27 described.Verified on v1.17.7 (Linux): 8 concurrent
opencode runone-shots → 8/8 correct outputs, zerodatabase is locked/locking protocolerrors.Caveats:
--continue/--sessionwon't find it afterwards. Anything needing session continuity should keep the default dir (and run sequentially);PRAGMA busy_timeoutas proposed above would be the proper solution.akostadinov commentedon Aug 28, 2026
Yes, please allow overriding
OPENCODE_DATA_DIRas an environment variable as well as a configuration option. Overriding the wholeXDG_DATA_HOMEis super heavy handed.efirvida commentedon Sep 21, 2026
Additional occurrence with WAL-generation forensics — NFSv4 home, killed process and concurrent instances, two incidents (Sep 19 & 20, 2026). Same defect class as this issue; adding evidence it currently lacks.
Environment
vers=4.0,hard,rsize=1048576,wsize=1048576,proto=tcp,timeo=600,retrans=2,sec=sys,local_lock=none.~/.local/share/opencode/opencode.db(SQLite WAL mode, Drizzle/bun-sqlite stack).Triggers observed
Corruption evidence (salvaged copy)
PRAGMA quick_check:WAL forensics (quarantined
-wal, 25,717,072 bytes, 6,242 frames @ 4096)page_sizefield reads0x002de218while frame geometry proves the real page size is 4096.Reproducibility of the fingerprint
The same interleaved-salt fingerprint (7 salts / 1,676 frames, unparseable header, 0/200 frames valid) is present in a
-walon a local ext4 machine after a kill — there the main DB stayed intact because SQLite ignored the broken WAL. So the pattern is not NFS-exclusive, but only the NFS deployment produced main-DB btree corruption and data loss.Impact
file is not a database/ blank UI on next start.sqlite3 .recoverSQL dump (~189 MB) + rebuild; failure is silent until restart.Suggested hardening (additions to this thread)
statfsNFS magic 0x6969, or CIFS/SMB) and either refuse WAL (journal_mode=DELETE+synchronous=FULL) or fail loudly with an actionable message.PRAGMA quick_check) with automatic quarantine of-wal/-shmand recreation instead of crashing onfile is not a database.PRAGMA busy_timeout+ retry (ties into Concurrent opencode processes on one database fail prompts with "Failed to execute statement" (SQLITE_BUSY) #47566 / Daily "Failed to execute statement" on macOS with concurrent opencode processes (1.18.30, busy_timeout=0, 21 GB DB) #48416).Quarantined
-wal/-shmand DB copies are retained and available for maintainer analysis.thdxr commentedon Oct 11, 2026
Thanks for the report! OpenCode 2.0 is now out and development is focused on V2, so we're closing issues filed against OpenCode 1. Please upgrade (
opencode upgrade --major, details: https://anoma.ly/notes/opencode-2-0), and if this is still relevant on 2.0, reopen this issue or open a new one with your 2.0 version.— from 𝕺𝖕𝖊𝖓𝕮𝖔𝖉𝖊