Repository navigation
Description
Description
On the Nix build, opencode silently drops its native filesystem watcher and falls back to degraded watching.
The bundled @parcel/watcher addon is a prebuilt .node that links libstdc++.so.6 dynamically, with no RUNPATH.
On NixOS the loader can't resolve that library, so the dlopen fails and the watcher binding is not registered.
I see this both in the packaged output (nix build .#opencode) and in the flake.nix devshell.
Neither puts the C++ runtime on LD_LIBRARY_PATH, so the addon can never find it.
In practice the symptom is behavioral: edit a skill (or a config) and the running session never picks it up.
Recursive directory watches go silent, while the per-file watches still work.
I think this is the same defect as #18447 (which already proposed adding stdenv.cc.cc.lib to the wrapper) and #28964 (same failed to load watcher binding line).
Both were closed by the stale bot and it still reproduces, so I'm re-reporting with a narrower repro and a fix.
Plugins
(none)
OpenCode version
2.0.22 (d1f8f5b)
Steps to reproduce
The failure is swallowed at load time, so the behavioral repro is the clearest one:
- On NixOS, start opencode — either
bun devfrom the devshell, or the packaged binary fromnix build .#opencode. - Edit a skill under a watched skills directory, e.g.
~/.config/opencode/skills/<name>/SKILL.md. - The skill does not reload.
The recursive watch on the skills directory silently delivers no events, so the plugin never rescans.
To surface the underlying failure, add a temporary log to packages/core/src/filesystem/watcher.ts (revert afterwards):
const watcher = lazy((): typeof ParcelWatcher | undefined => {
try {
return createWrapper(loadBinding()) as typeof ParcelWatcher
} catch (error) {
console.error("[watcher] native binding failed to load", error)
return
}
})It prints libstdc++.so.6: cannot open shared object file.
The server log shows the same thing from the other side — the recursive watch gives up:
message="watcher subscribe" path=~/.config/opencode/skills type=directory ignores=0
level=ERROR message="watcher backend not supported" directory=~/.config/opencode/skills platform=linux
Note the per-file watches (type=file / type=entries) still start with backend=node, so only the recursive directory watches silently break.
The devshell has the same gap, so bun dev fails there too.
Screenshot and/or share link
(none)
Operating System
NixOS
Terminal
WezTerm
Activity
github-actions commentedon Oct 4, 2026
This issue appears to overlap with existing reports. Please check:
libstdc++.so.6runtime dependency.failed to load watcher binding/libstdc++.so.6error.luochen1990 commentedon Oct 27, 2026
Adding production evidence from a long-running NixOS deployment (2.0.26,
0d61729b3f, Nix-packaged,opencode serve --service):The daemon log shows
watcher backend not supported1333 times over a ~21-hour window (~1/min — it retries on every watch attempt). Verbatim except elided run/span ids and the project path:Behavioral impact beyond skills: directory-level changes — new/removed/swapped plugin entries, and config files (which, as observed, aren't covered by a working per-file watch either) — never hot-reload. Per-file watches on already-loaded plugin entries still fire (that's how the rescan in the issue we filed separately (Plugins are silently unloaded after a plugin-dir symlink swap is followed by any rescan — NixOS home-manager deployments #59725) gets triggered). The only thing that eventually picks up directory-level changes is the supervisor's periodic full rescan (~24h), so a long-running service silently runs with stale config/plugins for up to a day.
One timing note connecting to that separate issue (Plugins are silently unloaded after a plugin-dir symlink swap is followed by any rescan — NixOS home-manager deployments #59725): because the directory watcher is dead, a plugin-dir symlink swap is not picked up immediately — the unload we describe there is delayed until the ~24h fallback rescan. The dead watcher changes the timing and visibility of that bug (delayed, mysterious, logs silent); the unload itself is its own defect.
We now run a systemd path unit + idle gate that restarts the service on plugin-dir changes as a workaround, but a proper fix at the packaging layer (e.g. the
stdenv.cc.cc.libwrapper approach proposed in #18447 (linked upthread), or a static/bundled addon) would remove the whole class.opencodejr commentedon Oct 27, 2026
Thanks for the detailed report and the fix PR. I could reproduce this, but not on NixOS itself: I hid the system
libstdc++.so.6on a regular Linux x64 machine to imitate it.What I found on 2.0.22:
@parcel/watcheraddon needslibstdc++.so.6and has no RUNPATH. The opencode binary itself doesn't need it.~/.config/opencodedirectory watch starts withbackend=inotify.watcher backend not supported ... platform=linux. Thetype=entrieswatches still start withbackend=node.LD_LIBRARY_PATH(the approach in fix(nix): expose libstdc++ so the native fs watcher loads on NixOS #53213), the directory watch starts normally again.It still happens on the latest
dev(fd8268e) run from source: with libstdc++ hidden, both~/.config/opencodeand~/.config/opencode/skillslogwatcher backend not supported.nix/opencode.nixthere still only putswaylandonLD_LIBRARY_PATH, and the devshell adds no libstdc++.I only checked the error in the logs, not that an edited skill fails to reload. The script below runs these checks; it needs root because it temporarily moves the system libstdc++ and puts it back on exit.
Related: #18447 and #28964 (same missing libstdc++, closed as stale), #56255 (config edits not picked up on the Nix build, closed as a duplicate of this one) and #53213 (your fix).
Reproduction script (repro.sh)
Run it:
bash repro.sh