Repository navigation
Description
Title
tui: startup crash with EMFILE when inotify instances are exhausted (unguarded fs.watch in ConfigProvider)
Description
The TUI crashes at startup with a fatal error screen when the host's per-uid inotify instance quota is exhausted:
OpenCode crashed
An unexpected error stopped the session.
Error
EMFILE: too many open files, watch '~/.config/opencode'
Stack trace
Error: EMFILE: too many open files, watch '/home/<redacted>/.config/opencode'
at watch (unknown)
at new FSWatcher [internal/fs/watch:101:31]
at watch [internal/fs/watch:172:23]
at GG ($bunfs/root/chunk-pramjmve.js:3:54669)
at tn ($bunfs/root/chunk-pramjmve.js:3:7340)
at get ($bunfs/root/chunk-pramjmve.js:2:18355)
at children ($bunfs/root/chunk-2g9pk755.js:2590:3707)
at <anonymous> ($bunfs/root/chunk-pramjmve.js:2:15016)
at RA ($bunfs/root/chunk-pramjmve.js:2:10350)
at Ne ($bunfs/root/chunk-pramjmve.js:2:10130)
At startup the TUI calls fs.watch() in four places. Only one of them (the storage directory watcher) is guarded with try/catch — that guard landed on 2026-09-24 ("fix(tui): don't crash when fs.watch throws (e.g. ENOSPC)", "fix(tui): handle storage watcher errors after startup"). The other three are unguarded, so the synchronous EMFILE thrown by new FSWatcher() propagates through the SolidJS render tree and kills the whole TUI.
This is the TUI sibling of #16610 (server hangs at startup on inotify exhaustion). The server side was mitigated (.git watcher moved to fs.watchFile(), plus the OPENCODE_DISABLE_FILEWATCHER switch), but the TUI config watcher still hard-depends on inotify: no error handling, no fallback, no env switch.
OpenCode version
2.0.18 (@opencode/cli installed via bun install -g). The unguarded call sites are unchanged on the latest v2 branch.
Steps to reproduce
- Exhaust the per-uid inotify instance quota for the user running opencode. Easiest inside a shared container host: sibling containers of the same uid exhaust
fs.inotify.max_user_instances(kernel counts inotify instances per real uid across containers). Verify independently of opencode: a one-linefs.watch('/tmp', ...)in bun/node fails with EMFILE while the process itself is far below its fd limit.- Observed in the affected environment: container-local processes hold only 8 inotify instances out of
max_user_instances=128, per-process fd count is ~4 of soft limit 1024, andinotify_init1()still fails with EMFILE consistently.sysctl -winside the container is rejected (/proc/sys read-only).
- Observed in the affected environment: container-local processes hold only 8 inotify instances out of
- Run
opencode(TUI). - Crash screen appears immediately:
OpenCode crashed — EMFILE: too many open files, watch '~/.config/opencode'.
Note: the same binary works fine when an inotify instance is available; and older 2.0.x releases crash identically in this state — the crash follows the environment, not a specific 2.0.x regression.
Expected behavior
- A failed
fs.watch()(EMFILE / ENOSPC / ENOSYS) should degrade gracefully: log a warning, disable config hot-reload, and keep the TUI usable — same treatment as the storage watcher fix. - Ideally the TUI watchers honor an env switch (mirroring the server-side
OPENCODE_DISABLE_FILEWATCHER) so users in constrained containers can opt out entirely.
Actual behavior
Fatal crash screen at startup; the TUI is completely unusable while the server and web clients keep working.
Operating system
Linux (Docker container; host kernel 3.10 / CentOS 7; fs.inotify.max_user_instances=128 shared per-uid across containers on the same host)
Plugins
None — stock install; the crash happens during startup before plugins load.
Proposed fixes
- Option A (minimal, consistent with the storage fix): wrap the three remaining unguarded
watch()calls in try/catch and continue with live-reload disabled on failure:packages/tui/src/config/index.tsx:357(watch(path.dirname(host.path))in ConfigProvider — the crash site)packages/tui/src/model-preference.ts:155packages/tui/src/plugin/watch.ts:47
- Option B: centralize a
safeWatch()helper used by all TUI watchers, so future watch sites inherit the guard. - Option C (ergonomics): honor
OPENCODE_DISABLE_FILEWATCHER(already supported by the server) in the TUI as a full opt-out.
Activity
github-actions commentedon Sep 26, 2026
This issue might be a duplicate of existing issues. Please check:
EMFILEwhile watching the state directory.ENOSPC).ENOSPC).fix(tui): degrade gracefully when fs.watch fails
fix(tui): degrade gracefully when fs.watch fails