[Bug] Bun: live ContentStore/SessionDB handle wedges to permanent SQLITE_IOERR (disk I/O error) on healthy disk+DB; ctx_doctor stays green

Author: halindromeCreated Jul 23, 2026Updated Sep 17, 2026

Summary

Under Claude Code on the Bun runtime, a single context-mode MCP server process can get wedged: every live ctx_search / ctx_index / ctx_fetch_and_index returns SQLITE_IOERR ("disk I/O error"), reads and writes alike, and stays that way for the life of the process. Meanwhile the disk is healthy, the on-disk DB files are not corrupt (quick_check/integrity_check = ok), ctx_doctor reports all-green, and a different context-mode server process on the same machine works fine. The only recovery is restarting that MCP server.

This looks like the same root cause as #880 (ContentStore singleton poisoned to a permanent disk I/O error by a cleanupStaleContentDBs race across parallel MCP servers) and #905 (Bun bun:sqlite SQLITE_IOERR), observed here from Claude Code rather than the Pi bridge. Filing with a clean OS-level reproduction + a ctx_doctor gap; please dedupe into #880/#905 if appropriate.

Environment

  • context-mode 1.0.169 (Claude Code plugin form)
  • Runtime: Bun 1.3.10 (/opt/homebrew/bin/bun … /1.0.169/start.mjs)
  • macOS 26.5.2 (build 25F84), arm64
  • 4 concurrent context-mode … start.mjs processes running (multiple open Claude Code sessions across projects)

Symptom

In one session, every live store operation fails identically:

ctx_search            → disk I/O error (SQLITE_IOERR)
ctx_index (tiny payload) → disk I/O error
ctx_fetch_and_index   → disk I/O error

Both reads and writes fail — so it is not a write-path/busy_timeout issue (cf. closed #667/#668) and not content-specific.

What was ruled out (evidence)

Check Result
Disk space / inodes ✅ 125 GiB free, inodes 1%, storage dir writable
ctx_doctor ✅ green — FTS5/SQLite PASS, server test PASS
PRAGMA quick_check / integrity_check on every content + session DB ok
Fresh sqlite3 CLI wal_checkpoint(TRUNCATE) on the active DB ✅ `0
Live ctx_* ops in the affected server ❌ every read & write → SQLITE_IOERR
Same ctx_index from a different session's server, same machine ✅ succeeds

Tell-tale sign of a wedged live handle: the active content DB's WAL grew during observation (43c36a…​.db-wal 580 KB → 1.55 MB) and never checkpointed, while a fresh CLI handle to the same file checkpoints clean and passes quick_check. The data is intact; the connection is broken.

Likely mechanism

The live ContentStore (and/or SessionDB) bun:sqlite connection is poisoned into a persistent SQLITE_IOERR and never reopened. server.ts runs cleanupStaleContentDBs(contentDir, 14) on startup (≈ server.ts:728–729); with several servers alive, one server's startup sweep / another's handle can interact exactly as #880 describes, but here under Claude Code + Bun.

Two gaps make this worse than it needs to be:

  1. No self-heal on IOERR. isSQLiteCorruptionError() (db-base.ts) matches SQLITE_CORRUPT/SQLITE_NOTADB/"malformed"/"not a database" — it does not match SQLITE_IOERR, so the rename-and-recreate / reopen recovery path never triggers. A live handle throwing IOERR on a provably-healthy file should reopen the connection (and, if it recurs, surface an actionable error) rather than fail every op forever.
  2. ctx_doctor blind spot. ctx_doctor exercises a scratch/:memory: DB, so it reports green while the live ContentStore/SessionDB handle is wedged — producing the confusing "context-mode says everything's fine, yet nothing works" state. ctx_doctor should probe the actual live handle with a trivial read/write and report it separately.

Workaround

Restart the affected MCP server — /mcp reconnect context-mode, or quit/reopen the session. The healthy DB files reattach cleanly and the 1.5 MB WAL checkpoints on first open. No ctx_purge / rebuild needed — the store is not corrupt.

Secondary: the error message misleads agents

A bare disk I/O error reads like a hardware/disk fault. In practice it repeatedly led an agent to conclude "the sandbox/disk is broken" and route around context-mode entirely (curl → temp file → read raw bytes into context), defeating the point of the tool. When a live-handle IOERR hits a healthy file/disk, the surfaced message should say so and recommend restarting the server, e.g. "context-mode server connection wedged (disk and DB are healthy) — restart the MCP server (/mcp reconnect)."

Related

  • #880 — cleanupStaleContentDBs race poisons ContentStore singleton → permanent disk I/O error (likely same root cause)
  • #905 — Bun/bun:sqlite same-turn ctx_indexctx_search SQLITE_IOERR
  • #985 — multi-session contention on shared per-project content DB
  • #667 / #668 (closed) — earlier ctx_fetch_and_index "disk I/O error" (write-path/busy_timeout, node:sqlite) — distinct: this one fails reads too and is Bun