You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Opencode hangs at startup if a .git repo is present and inotify user instances run out #16610
Also affected — NixOS, opencode-desktop 1.3.3, 102% CPU on FileWatcher.init
I'm experiencing the exact same issue on the desktop GUI (not TUI). The TUI works perfectly fine — it's specifically the desktop's bundled sidecar (opencode-cli serve) that spins at 102% CPU indefinitely.
inotify settings:max_user_instances=524288, max_user_watches=524288 (already very high)
Symptoms
The desktop sidecar's FileWatcher.init enters a tight CPU loop on every project directory and worktree simultaneously. Every single call times out:
ERROR service=file.watcher dir=/home/micqdf cause=TimeoutError: TimeoutError
at FileWatcher.state (index.js:63114:57)
at FileWatcher.init (index.js:23526:109)
at FileWatcher.init (definition) (index.js:236755:32) failed to subscribe
This repeats for:
The home directory (/home/micqdf)
All project directories (~/nixos/nix-config, ~/github/...)
All .git directories within projects
All worktree directories under ~/.local/share/opencode/worktree/
The sidecar never recovers — it's stuck at 102% CPU in this loop and never reaches the point of sending LLM API requests. The desktop GUI shows infinite "thinking" on all models.
Key observation
The system inotify limits are already set very high (524288). The issue isn't system-level inotify exhaustion — it's the FileWatcher implementation itself timing out on every directory regardless. The watcher.ignore config option also has no effect on the desktop sidecar (only works for TUI/CLI).
Workaround
Only the TUI works. No config-based workaround has been effective for the desktop.
adding the following to config allowed opencode to startup for me. +1 to clear error message, this is painful since the process completely hangs with a black screen.
One of my coworkers was having the same issue. They use GoLand / PyCharm which use inotify extensively. They ran sysctl fs.inotify.max_user_instances and it's set to 128. Closing GoLand / PyCharm fixed the issue.
Just another confirmation. Got this in multiple repos, including a brand new directory that opencode worked fine in before git init ; git commit --allow-empty -m "Initial root commit". Increasing max_user_instances fixed it.
opencode run "hello" works, so the model/agent path is fine.
A non-Git directory under /workspaces works.
An empty Git repo under /workspaces crashes.
A fake Git env also works: GIT_DIR=/tmp/no-git-dir GIT_WORK_TREE=/tmp/no-git-work-tree opencode
Watcher usage was not near kernel limits during testing (roughly 7k–13k watches, 5 inotify instances; limits 65,536 / 128), so this is not only active global inotify exhaustion.
The crash signature is exit code 132 (SIGILL), Bun panic: Segmentation fault at address 0x0.
I’d like to submit a fix: watcher initialization should not block or kill TUI startup. If watcher setup fails, OpenCode should log a warning, disable the watcher, and continue running.
Update: This PR (#32854) has been closed for further investigation. After deeper analysis of @parcel/watcher's C++ source, we found the .git watcher crash is in the native subscribe() call itself (SIGILL/SIGSEGV at address 0x0), not in std::regex_match as initially suspected. The JS-level error handling improvements in the PR are valid but cannot catch a native crash. We're systematically reproducing the crash to isolate the exact failure point and will open a new PR with the correct fix.
Original comment:
I've opened PR #32854 with a fix for this. The core issue is that the file watcher layer had no error isolation — any failure in the .git watcher setup (git.dir timeout, subscribe timeout, readDirectoryEntries failure) would propagate to the outer catchCause and return an empty service, killing the entire watcher including the TUI.
The fix makes the watcher non-fatal at the JS level:
Effect.promise → Effect.tryPromise — catches synchronous throws from the native @parcel/watcher binding, not just promise rejections
.git watcher isolated into its own forkScoped with catchCause — git watcher failures no longer kill the entire watcher layer; only git HEAD tracking is lost
Callback error parameter now logged — @parcel/watcher reports backend errors through the callback's first parameter; these were silently discarded
Improved catchCause logging — makes it clear the watcher was disabled, not just that something failed
Suppress unsubscribe errors — prevents unhandled rejections during cleanup
Important caveat: This fixes JS-level errors in the watcher, but the native segfault (std::regex_match crash in @parcel/watcher on long paths with glob ignore patterns — see parcel-bundler/watcher#250) cannot be caught from JS. A follow-up PR should filter glob patterns out of the native watcher's ignore list and do glob matching in JS instead, which eliminates the native crash vector entirely.
Two bugs in @parcel/watcher v2.5.1 cause the hang/crash:
Instance exhaustion → hang: When inotify_init1() fails, Backend::run() blocks forever on mStartedSignal.wait() because InotifyBackend::start() throws before calling notifyStarted(). No JS-level timeout can recover.
Watch exhaustion → crash: When inotify_add_watch() fails with ENOSPC, subscribe() throws a WatcherError that is not a subclass of std::exception, so it escapes all catch blocks and triggers std::terminate().
Additionally, on overlayfs (Docker dev containers), inotify_add_watch() can fail with ENOSPC even when the watch count is far below max_user_watches, and fs.watch() may silently succeed but never fire events.
Fix
Replace the .git watchers @parcel/watcher.subscribe() (which needed ~228 inotify watches to watch the entire .git/ directory tree) with fs.watchFile() on .git/HEAD (uses stat polling, zero inotify watches).
The VCS service only listens for HEAD changes for branch tracking (it filters if (!data.file.endsWith("HEAD")) return), so watching the entire .git/ tree was unnecessary.
fs.watchFile works on any filesystem regardless of inotify state, handles atomic file replacement (git writes to HEAD.lock then renames), and needs zero inotify watches. Branch changes are detected within ~2 seconds.
The root watcher (behind OPENCODE_EXPERIMENTAL_FILEWATCHER) continues using @parcel/watcher unchanged — its opt-in and experimental.
Verification
All 7 watcher tests pass, all 36 filesystem tests pass, typecheck passes
Reproduced the hang using LD_PRELOAD to intercept inotify_init1()
C diagnostic confirmed inotify_add_watch() fails with ENOSPC despite only 7119/65536 watches in use (overlayfs quirk)
Built standalone binary and tested on the problematic machine — opencode starts normally and branch switching is detected within ~2 seconds
Reacted by Lee B, OmniReality, Eric McDonald and 418-teapot
@OmniReality thanks for confirming the fix works on your machine too.
This is the most robust fix for this issue. The .git watcher was using @parcel/watcher to watch the entire .git/ tree, which requires inotify and has unfixable C++ bugs when resources are exhausted. Since the VCS service only needs HEAD changes for branch tracking, watching the entire tree was unnecessary. fs.watchFile on .git/HEAD uses stat() syscalls instead of inotify, so it works on any filesystem regardless of inotify state. The 2-second poll interval is negligible for branch switching.
The root watcher (behind OPENCODE_EXPERIMENTAL_FILEWATCHER) still uses @parcel/watcher. Only the always-active .git watcher was changed.
Activity
Christoph-D commentedon Mar 8, 2026
parcel-bundler/watcher#159 looks like the same issue. Perhaps this causes opencode to hang.
MichaelFisher1997 commentedon Mar 27, 2026
Also affected — NixOS, opencode-desktop 1.3.3, 102% CPU on FileWatcher.init
I'm experiencing the exact same issue on the desktop GUI (not TUI). The TUI works perfectly fine — it's specifically the desktop's bundled sidecar (
opencode-cli serve) that spins at 102% CPU indefinitely.Environment
opencode-desktop-unwrapped-1.3.3)opencode-cli --print-logs --log-level WARN servemax_user_instances=524288,max_user_watches=524288(already very high)Symptoms
The desktop sidecar's
FileWatcher.initenters a tight CPU loop on every project directory and worktree simultaneously. Every single call times out:This repeats for:
/home/micqdf)~/nixos/nix-config,~/github/...).gitdirectories within projects~/.local/share/opencode/worktree/The sidecar never recovers — it's stuck at 102% CPU in this loop and never reaches the point of sending LLM API requests. The desktop GUI shows infinite "thinking" on all models.
Key observation
The system inotify limits are already set very high (524288). The issue isn't system-level inotify exhaustion — it's the FileWatcher implementation itself timing out on every directory regardless. The
watcher.ignoreconfig option also has no effect on the desktop sidecar (only works for TUI/CLI).Workaround
Only the TUI works. No config-based workaround has been effective for the desktop.
EmpireJones commentedon May 8, 2026
I just ran into this same issue; I had to increase fs.inotify.* values to get opencode to start 👍
I agree that a clear error message is needed.
cfal commentedon May 11, 2026
adding the following to config allowed opencode to startup for me. +1 to clear error message, this is painful since the process completely hangs with a black screen.
docs: document linux inotify exhaustion anomalyco#16610
Phantal commentedon May 13, 2026
One of my coworkers was having the same issue. They use
GoLand/PyCharmwhich use inotify extensively. They ransysctl fs.inotify.max_user_instancesand it's set to 128. ClosingGoLand/PyCharmfixed the issue.lee-b commentedon Jun 3, 2026
Just another confirmation. Got this in multiple repos, including a brand new directory that opencode worked fine in before
git init ; git commit --allow-empty -m "Initial root commit". Increasing max_user_instances fixed it.dtinth commentedon Jun 8, 2026
Also encountering this issue. Right now I work around by increasing these to a massive number.
esaesa commentedon Jun 12, 2026
same for me.after clicking ctrl+c i get
sailod commentedon Jun 18, 2026
I can reproduce a closely related failure on OpenCode 1.17.8 / Bun 1.3.14 in a Linux dev container.
In my case, plain
opencodeblack-screens / crashes when run inside a Git repo under/workspaces, while this works:A few useful details:
opencode run "hello"works, so the model/agent path is fine./workspacesworks./workspacescrashes.GIT_DIR=/tmp/no-git-dir GIT_WORK_TREE=/tmp/no-git-work-tree opencodeSegmentation fault at address 0x0.I’d like to submit a fix: watcher initialization should not block or kill TUI startup. If watcher setup fails, OpenCode should log a warning, disable the watcher, and continue running.
lee-b commentedon Jun 18, 2026
This looks a lot like a null pointer dereference, which should never happen in bug-free code.
fix(core): tolerate file watcher startup failures
sailod commentedon Jun 18, 2026
Update: This PR (#32854) has been closed for further investigation. After deeper analysis of
@parcel/watcher's C++ source, we found the.gitwatcher crash is in the nativesubscribe()call itself (SIGILL/SIGSEGV at address 0x0), not instd::regex_matchas initially suspected. The JS-level error handling improvements in the PR are valid but cannot catch a native crash. We're systematically reproducing the crash to isolate the exact failure point and will open a new PR with the correct fix.Original comment:
I've opened PR #32854 with a fix for this. The core issue is that the file watcher layer had no error isolation — any failure in the
.gitwatcher setup (git.dir timeout, subscribe timeout, readDirectoryEntries failure) would propagate to the outercatchCauseand return an empty service, killing the entire watcher including the TUI.The fix makes the watcher non-fatal at the JS level:
Effect.promise→Effect.tryPromise— catches synchronous throws from the native@parcel/watcherbinding, not just promise rejections.gitwatcher isolated into its ownforkScopedwithcatchCause— git watcher failures no longer kill the entire watcher layer; only git HEAD tracking is lost@parcel/watcherreports backend errors through the callback's first parameter; these were silently discardedImportant caveat: This fixes JS-level errors in the watcher, but the native segfault (
std::regex_matchcrash in@parcel/watcheron long paths with glob ignore patterns — see parcel-bundler/watcher#250) cannot be caught from JS. A follow-up PR should filter glob patterns out of the native watcher's ignore list and do glob matching in JS instead, which eliminates the native crash vector entirely.sailod commentedon Jun 19, 2026
Ive opened PR #32930 with a fix for this.
Root cause
Two bugs in
@parcel/watcherv2.5.1 cause the hang/crash:Instance exhaustion → hang: When
inotify_init1()fails,Backend::run()blocks forever onmStartedSignal.wait()becauseInotifyBackend::start()throws before callingnotifyStarted(). No JS-level timeout can recover.Watch exhaustion → crash: When
inotify_add_watch()fails with ENOSPC,subscribe()throws aWatcherErrorthat is not a subclass ofstd::exception, so it escapes allcatchblocks and triggersstd::terminate().Additionally, on overlayfs (Docker dev containers),
inotify_add_watch()can fail with ENOSPC even when the watch count is far belowmax_user_watches, andfs.watch()may silently succeed but never fire events.Fix
Replace the
.gitwatchers@parcel/watcher.subscribe()(which needed ~228 inotify watches to watch the entire.git/directory tree) withfs.watchFile()on.git/HEAD(usesstatpolling, zero inotify watches).The VCS service only listens for HEAD changes for branch tracking (it filters
if (!data.file.endsWith("HEAD")) return), so watching the entire.git/tree was unnecessary.fs.watchFileworks on any filesystem regardless of inotify state, handles atomic file replacement (git writes toHEAD.lockthen renames), and needs zero inotify watches. Branch changes are detected within ~2 seconds.The root watcher (behind
OPENCODE_EXPERIMENTAL_FILEWATCHER) continues using@parcel/watcherunchanged — its opt-in and experimental.Verification
LD_PRELOADto interceptinotify_init1()inotify_add_watch()fails with ENOSPC despite only 7119/65536 watches in use (overlayfs quirk)OmniReality commentedon Jun 24, 2026
I encountered this bug and tried sailod’s fix and it worked!
I hope this issue would be resolved in the live version as well.
sailod commentedon Jun 25, 2026
@OmniReality thanks for confirming the fix works on your machine too.
This is the most robust fix for this issue. The
.gitwatcher was using@parcel/watcherto watch the entire.git/tree, which requires inotify and has unfixable C++ bugs when resources are exhausted. Since the VCS service only needs HEAD changes for branch tracking, watching the entire tree was unnecessary.fs.watchFileon.git/HEADusesstat()syscalls instead of inotify, so it works on any filesystem regardless of inotify state. The 2-second poll interval is negligible for branch switching.The root watcher (behind
OPENCODE_EXPERIMENTAL_FILEWATCHER) still uses@parcel/watcher. Only the always-active.gitwatcher was changed.github-actions commentedon Aug 25, 2026
To stay organized issues are automatically closed after 60 days of no activity. If the issue is still relevant please open a new one.