Repository navigation
Description
Summary
On Windows, the recursive file watcher (backend=node) follows directory junctions (reparse points) instead of treating them like symlinks. In a workspace containing many junctions that mirror large subtrees, the watcher's effective tree explodes into millions of entries, and sessions hang in "working" forever. The watcher should not descend into reparse points (git with core.symlinks=true already treats them as symlinks and does not descend), or at minimum watcher.ignore semantics should keep the watcher from recursively re-walking mirrored trees.
Environment
- opencode version: v2.0.12
- OS: Windows_NT 10.0.26200 (win32 x64)
- Terminal:
TERM=xterm-256color;TERM_PROGRAM/COLORTERMunset (Windows) - Shell:
C:\Windows\system32\cmd.exe(opencodeshellsetting:powershell) - Install/channel: latest (release)
- Active plugins: none found in config (
~/.config/opencode/opencode.jsonccontains only{"shell": "powershell"})
Reproduction
Minimal:
- Create a workspace with a large directory
big/(~50k files is enough to observe the slowdown). - Create two scaffold directories, each containing junctions that mirror
big/and its subdirectories:
mklink /J repro\a\big repro\bigandmklink /J repro\b\big repro\big(and a few more nested ones). - Open the workspace in opencode and start a session.
Real-world trigger (my case): a Godot project (233k files / 143 GB) whose QA harness generates verification_project/mandatory_suite_project scaffold trees under artifacts/. Each scaffold re-links game/ subdirectories via junctions — 1,233 junctions total, mirroring game/.godot (12,146 files / 9 GB) and game/assets (15,630 files / 8 GB) dozens of times each.
Expected Behavior
The watcher skips directory junctions/reparse points when doing recursive discovery (consistent with git, which runs with -c core.symlinks=true and treats them as symlinks), so watcher startup cost is proportional to the real tree. Sessions start responding promptly.
Actual Behavior
- Session requests stay in "working" indefinitely; the first token never arrives.
- The log shows the watcher starting with zero ignores and full recursion, e.g.:
message="watcher subscribe" path="...\\AGENTS.md" type=file ignores=0 message="watcher started" path="...\\.git\\HEAD" type=file backend=node ignores=0 - The shared background service thrashes and gets restarted; the client log accumulates uncaught exceptions:
level=ERROR message="uncaught exception" cause="Error: EPIPE: broken pipe, write" origin=uncaughtException role=cli - Meanwhile
git statusin the same workspace completes in ~0.7 s, because git does not descend through the junctions — which isolates the problem to the watcher's traversal, not workspace size per se.
Additional Context
- Frequency: consistent — every session in this workspace, on v2.0.12.
- Workaround that resolves it: adding a
watcher.ignoreblock inopencode.jsonccovering the junction-heavy directories ("artifacts/**","game/.godot/**", etc.). With those ignored, sessions respond normally. This works but must be maintained manually, and any new directory containing junctions reintroduces the hang. - Suggested fix: when doing recursive watch discovery on win32, skip entries with the
FILE_ATTRIBUTE_REPARSE_POINTattribute (or only follow true symlinks), matching git'score.symlinksbehavior. Optionally also seedwatcher.ignorefrom.gitignore. - Side observation (same workspace): one junction pointed sideways at a sibling directory inside the watched tree (
artifacts\hit_palette\mandatory_suite_project -> artifacts\hit_palette\verification_project_full), which makes naive traversal re-walk subtrees multiplicatively even without a cycle; a junction cycle would make it unbounded, so reparse-point skipping is the robust fix.
Activity
github-actions commentedon Sep 22, 2026
This issue might be related to an existing issue involving Windows reparse points and the file watcher. Please check:
Neither is an exact duplicate of your report (junction-mirrored trees causing watcher explosion and session hangs), but they are in the same problem space and may be worth cross-referencing with maintainers.
opencodejr commentedon Oct 28, 2026
Thanks for the detailed report and the junction setup. I couldn't reproduce this here: it depends on NTFS directory junctions, and our reproduction sandbox runs Linux, so it needs a maintainer on Windows.
One note for whoever picks this up: in v2.0.12 the file watcher doesn't watch the workspace recursively. It only watches
.git/HEADand a few individual files, which is what thebackend=nodelog lines show. The more likely thing walking the junctions is the built-in file-search index, which in v2.0.12 starts scanning the whole workspace when it opens. In later releases it waits until the first search, but it still scans the whole tree then. Snapshot capture before the model call is another possibility, but it goes through git, which doesn't follow junctions.If you can, it would help to know:
fff) or snapshot capture around the time the session gets stuckRelated issues:
$HOME, closed as a duplicate of core: session never drains when Snapshot.capture() stalls on huge untracked tree — git add hangs, execution claim never released #48657