Skip to content

Bug: file watcher paths stop matching open files on Windows #17780

Closed as not planned

Description

@jb-sk

What happened?

On Windows, file watcher updates can fail to refresh both:

  • files shown in the file tree
  • files already open in tabs

This happens when watcher events use backslash-separated paths while the app store / tab state uses slash-separated paths.

As a result, external file changes or tool-driven edits can be missed even though the watcher event itself arrives.

In my testing, this is not limited to unloaded descendants. Directory contents can also fail to refresh even when the relevant directory is already visible.

Steps to reproduce

  1. Run OpenCode on Windows.
  2. Open a file in a tab.
  3. Modify that file through a tool action or an external process.
  4. Observe that the open tab content does not always refresh.
  5. Create, remove, or change files in a directory shown in the file tree.
  6. Observe that directory contents may fail to refresh in the file tree, including cases where the directory is already visible.

Expected behavior

  • Watcher paths should match store/tab paths consistently on Windows.
  • Open files should refresh when watcher updates arrive.
  • File tree refresh should work reliably when directory contents change.

Actual behavior

  • Watcher updates can be ignored because normalized paths do not match.
  • Open tabs stay stale.
  • File tree refresh can be skipped or missed when directory contents change.

Suspected cause

normalize() can preserve Windows \ separators for watcher-originated paths, while other app paths are compared in / form.

There also appears to be a watcher refresh gap in directory invalidation / refresh behavior, not just a path-key mismatch.

Proposed fix

  • Normalize watcher path comparisons consistently so Windows paths match the existing store/tab keys.
  • Make watcher-driven directory refresh behavior reliable when file system changes occur, including directories that are already visible.

Additional context

This may be related to the separator-preserving behavior introduced for Windows / Cygwin compatibility in PR #14912:
#14912

However, the current behavior appears to break watcher-to-store comparisons in the app UI and also misses some file tree refresh cases.

Activity

  1. added
    coreAnything pertaining to core functionality of the application (opencode server stuff)
    on Mar 16, 2026
  2. github-actions commented on Mar 16, 2026

    @github-actions
    Contributor

    This issue might be a duplicate of (or closely related to) existing issues around Windows path separator normalization. Please check:

    These issues share the same root cause (Windows \ vs / path normalization), though #17780 specifically targets the file watcher → store/tab comparison path. If the fix scope overlaps, it may make sense to consolidate.

  3. jb-sk commented on Mar 16, 2026

    @jb-sk
    Author

    Thanks, I checked those issues.

    They do seem to share the same broader Windows path canonicalization root cause (\ vs /, and possibly drive-letter normalization), especially for the path comparison part.

    That said, this issue is focused on a narrower but still distinct failure path in the app UI:

    • watcher event path -> store/tab path comparison for already-open files
    • watcher-driven file tree refresh behavior

    In my testing, this is not limited to unloaded descendants. Directory contents can also fail to refresh even when the relevant directory is already visible.

    So I think this issue is related to #12940, #14570, and #17183, but not fully covered by them unless the eventual fix is intentionally broadened to include the watcher/store-tab comparison path and the watcher-driven directory refresh path as well.

  4. jb-sk commented on Mar 18, 2026

    @jb-sk
    Author

    Leaving a detailed status note here in case it helps the next person who looks at this.

    I checked origin/dev again before writing this comment. As of today, this issue still appears unresolved in the tree:

    • packages/app/src/context/file/path.ts still preserves native Windows separators in normalize()
    • packages/app/src/context/file/watcher.ts still bails out on add / unlink when the immediate parent is not loaded instead of walking up to the nearest loaded ancestor
    • the issue is still open

    I attempted an upstream fix in PR #17794 and ended up closing it after multiple rounds because I could not get it to a trustworthy state within a reasonable cost budget.

    What I changed in that attempt

    1. Watcher / open-file path normalization on Windows

      • normalized watcher-side paths so \ and / stop diverging between watcher events and store/tab keys
      • adjusted directory invalidation so file tree refresh could walk upward to the nearest loaded ancestor instead of only refreshing the immediate parent
    2. Follow-up fix for Windows e2e failure

      • the PR initially triggered a Windows e2e failure in e2e/prompt/prompt-mention.spec.ts
      • I reproduced that locally and traced it to two things:
        • file search could return an empty result before the initial file index finished
        • Windows file search / fuzzy matching was still inconsistent between \ and /
      • I added a fix so the first search waits for initial indexing and the search side compares normalized separators consistently
      • after that, the Windows @mention failure passed in CI
    3. Follow-up fix for Linux e2e failure

      • after the Windows failure was fixed, CI still failed on Linux in e2e/session/session-composer-dock.spec.ts, specifically blocked question flow unblocks after submit
      • that one was not failing in app logic; it was timing out while seeding a pending question request for the test
      • I traced that to the test using a live model-driven seed path for question requests, while the sibling permission tests already used deterministic route mocks
      • I rewrote the question-dock tests to use mocked /question list/reply/reject endpoints so the test only exercised the dock behavior instead of depending on an LLM/tool round-trip
      • that spec passed locally after the change

    Why I closed the PR anyway

    • even after the Windows mention fix and the Linux question-dock stabilization attempt, I still could not get the overall PR into a state where I was confident enough to keep asking CI to validate guesses
    • my local Windows environment also had unrelated noise that made iteration expensive:
      • the known symlink problem around packages/app/src/custom-elements.d.ts and packages/enterprise/src/custom-elements.d.ts
      • local full e2e instability unrelated to the original watcher bug
    • at that point I had already spent far too much token / debugging budget on the submission and stopped rather than continuing to churn CI with low confidence

    What I was able to confirm with high confidence

    • the original watcher/open-tab problem is real
    • normalize() preserving native separators on Windows is definitely part of the problem
    • watcher refresh behavior also has a separate gap for directory invalidation / refresh
    • fixing the watcher bug exposed or intersected with at least one additional Windows path/search issue in prompt-mention.spec.ts
    • there was also a flaky Linux e2e in the same PR attempt, but that one looked like test seeding instability rather than the watcher bug itself

    What I was not able to deliver confidently

    • a final upstream PR that fixed the watcher issue and also left the full CI suite in a clearly green, low-risk state
    • a clean separation between "watcher fix side effects" and "pre-existing flaky CI" that I would be comfortable asking maintainers to trust without further iteration

    If someone takes another run at this, my recommendation would be:

    1. keep the watcher/path fix itself as small as possible
    2. treat Windows watcher normalization and directory refresh as the primary bug
    3. expect prompt-mention.spec.ts to be a likely regression surface on Windows
    4. if Linux session-composer-dock.spec.ts fails again, treat that as a separate flaky-test cleanup rather than evidence that the watcher fix is conceptually wrong
    5. rebuild the PR from a fresh branch rather than reviving fix(app): normalize watcher paths on Windows #17794 directly

    Hope this saves somebody else some time.

  5. removed
    bugSomething isn't working
    coreAnything pertaining to core functionality of the application (opencode server stuff)
    on May 3, 2026
  6. github-actions commented on Jul 7, 2026

    @github-actions
    Contributor

    To stay organized issues are automatically closed after 60 days of no activity. If the issue is still relevant please open a new one.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions