{"record":{"id":"5781284a46f9e197","repo":"microsoft/typescript-go","slug":"fswatch-watch-terminated","errorCode":null,"errorMessage":"fswatch: watch terminated","messagePattern":"fswatch: watch terminated","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/fswatch/watcher.go","lineNumber":37,"sourceCode":"// errRootPath is returned by WatchFile when the supplied path is a\n// filesystem root with no parent directory to watch.\nvar errRootPath = errors.New(\"fswatch: cannot watch a root path\")\n\n// errNotAbsolute is returned by [Watcher.WatchDirectory] and\n// [Watcher.WatchFile] when the supplied path is not absolute.\nvar errNotAbsolute = errors.New(\"fswatch: path must be absolute\")\n\n// ErrOverflow indicates that the kernel event queue overflowed and\n// some filesystem changes were missed. The watch remains\n// active; further events will continue to be delivered. Callers\n// should treat this as a signal to rescan the watched directory.\nvar ErrOverflow = errors.New(\"fswatch: event overflow; some changes were missed\")\n\n// ErrWatchTerminated indicates that the watch was terminated due to\n// an unrecoverable error (e.g. the watched directory was deleted or\n// the watch descriptor was revoked). No further events will be\n// delivered. Call Close to release remaining state.\nvar ErrWatchTerminated = errors.New(\"fswatch: watch terminated\")\n\n// ErrUnavailable indicates that a requested watcher is not\n// available on the current platform.\nvar ErrUnavailable = errors.New(\"fswatch: watcher not available on this platform\")\n\n// ErrFilesystemUnsupported indicates that the active watcher backend cannot\n// operate on the target filesystem, even though the backend is available on\n// the current platform. This happens, for example, with the fanotify backend\n// on filesystems that do not support FID-based watching: name_to_handle_at\n// returning EOPNOTSUPP (some Docker bind mounts backed by virtiofs, gRPC FUSE,\n// or overlayfs) or fanotify_mark returning ENODEV (e.g. NTFS mounted via\n// fuseblk).\nvar ErrFilesystemUnsupported = errors.New(\"fswatch: watcher backend unsupported on this filesystem\")\n\n// Watcher represents a filesystem watching implementation.\n// Use one of the constructor functions ([Inotify], [FSEvents], [Kqueue],\n// [Windows]) to obtain a value, or [Default] for the platform default.\n//","sourceCodeStart":19,"sourceCodeEnd":55,"githubUrl":"https://github.com/microsoft/typescript-go/blob/1bcfa18d79a3be41772223d5c05dfe4480e614ff/internal/fswatch/watcher.go#L19-L55","documentation":"Exported terminal sentinel: the watch is dead and delivers no further events. It is produced when the watched directory is deleted (backends wrap 'watched directory removed' with it), the descriptor is revoked, or a backend hits an unrecoverable error routed through handleWatcherError, which wraps any dirWatchError with it. Delivered on the callback; the documented recovery is to handle it and re-subscribe if the target returns, and to call Close to release state.","triggerScenarios":"Deletion or rename-away of the watched root on any backend. Revoked handles (kqueue NOTE_REVOKE, for example an unmounted filesystem). Fatal Windows backend errors such as GetOverlappedResult failures or unknown completion errors.","commonSituations":"Build clean steps removing output directories. Removable media or network mounts unmounted under the watch. Container filesystems torn down at shutdown. WatchFile on a file whose parent directory is removed (no automatic recovery, unlike polling).","solutions":["Check errors.Is(err, fswatch.ErrWatchTerminated) in every callback and call Close() on the Watch","Re-subscribe once the target exists again (poll for existence, then WatchDirectory)","Watch a stable parent and filter child events for directories with delete/recreate cycles","Escalate to a polling watcher for paths where re-watching is unreliable"],"exampleFix":"// before\nwatch, _ := w.WatchDirectory(dir, cb) // callback ignores err\n\n// after\nwatch, _ := w.WatchDirectory(dir, func(events []fswatch.Event, err error) {\n    if errors.Is(err, fswatch.ErrWatchTerminated) {\n        watch.Close()\n        go resubscribeWhenDirExists(dir)\n        return\n    }\n    cb(events, err)\n})","handlingStrategy":"try-catch","validationCode":null,"typeGuard":"func isWatchTerminated(err error) bool {\n    return errors.Is(err, fswatch.ErrWatchTerminated)\n}","tryCatchPattern":"func(events []fswatch.Event, err error) {\n    if errors.Is(err, fswatch.ErrWatchTerminated) {\n        watch.Close()\n        go resubscribeWhenTargetExists(target)\n        return\n    }\n    apply(events)\n}","preventionTips":["Close terminated watches so per-directory state is released","Keep state rebuildable via full rescan for every watch you own","Prefer watching stable ancestors over delete/recreate targets","Remember WatchFile has no polling fallback: parent deletion is terminal"],"tags":["terminal","watch-lifecycle","filesystem","cross-platform"],"backgroundTag":null,"analyzedSha":"1bcfa18d79a3be41772223d5c05dfe4480e614ff","analyzedAt":"2026-08-16T02:12:00.115Z","schemaVersion":2},"datasetVersion":"2026-08-16T03:17:38.424Z"}