{"record":{"id":"0b5f76262442631b","repo":"microsoft/typescript-go","slug":"w-w-0b5f76","errorCode":null,"errorMessage":"%w: %w","messagePattern":"%w: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/fswatch/watcher.go","lineNumber":768,"sourceCode":"\t}\n\tb.mu.Unlock()\n\treturn nil\n}\n\nfunc (b *watcherBase) watchRemove(w *dirWatch) {\n\tb.mu.Lock()\n\tif _, ok := b.subscriptions[w]; !ok {\n\t\tb.mu.Unlock()\n\t\treturn\n\t}\n\tdelete(b.subscriptions, w)\n\t_ = b.self.closeWatch(w)\n\tb.mu.Unlock()\n}\n\nfunc (b *watcherBase) handleWatcherError(werr *dirWatchError) {\n\tb.watchRemove(werr.dirWatch)\n\twerr.dirWatch.notifyError(fmt.Errorf(\"%w: %w\", ErrWatchTerminated, werr))\n}\n\n// ----- dirWatch: per-directory watch state -------------------------\n\ntype callback struct {\n\tid               uint64\n\tdir              string\n\tphysicalDir      string\n\twatchDir         string\n\twatchPhysicalDir string\n\trecursive        bool\n\tfn               WatchCallback\n\tignore           func(path string) bool\n\tsinceSeq         uint64\n\tterminal         error\n\tdelivered        bool\n}\n","sourceCodeStart":750,"sourceCodeEnd":786,"githubUrl":"https://github.com/microsoft/typescript-go/blob/1bcfa18d79a3be41772223d5c05dfe4480e614ff/internal/fswatch/watcher.go#L750-L786","documentation":"The generic terminal wrap: watcherBase.handleWatcherError takes any backend *dirWatchError, removes the watch via watchRemove, and delivers fmt.Errorf(\"%w: %w\", ErrWatchTerminated, werr) to the directory's callbacks. Because both layers unwrap, errors.Is matches ErrWatchTerminated as well as the underlying backend error. This is the delivery path for fatal errors once a watch is running (Windows fatal(), backend subscription failures surfaced asynchronously).","triggerScenarios":"A running watch hits an unrecoverable backend error: Windows beginRead, GetOverlappedResult, or unrecognized completion errors routed through fatal(); kqueue or other backend dirWatchErrors delivered after the watch started. The callback receives ErrWatchTerminated with the backend error as the wrapped cause.","commonSituations":"Network shares dropping or removable media ejected mid-watch. Resource exhaustion that only appears after the watch has been alive for a while. Handle invalidation by filter drivers.","solutions":["In the callback, branch on errors.Is(err, fswatch.ErrWatchTerminated) and call Close() on the Watch","Drill into the wrapped cause (errors.Unwrap, errors.As on syscall.Errno) and log it to identify the real failure","Fix the environmental cause (limits, share availability, permissions) before re-subscribing","Re-subscribe with WatchDirectory once the cause is resolved"],"exampleFix":"// before\nfunc(events []fswatch.Event, err error) {\n    if err != nil {\n        log.Printf(\"watch error: %v\", err) // no recovery, watch leaks\n    }\n}\n\n// after\nfunc(events []fswatch.Event, err error) {\n    if errors.Is(err, fswatch.ErrWatchTerminated) {\n        log.Printf(\"watch terminated: %v\", errors.Unwrap(err))\n        watch.Close()\n        go resubscribeWhenReady(dir)\n        return\n    }\n    apply(events)\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        cause := errors.Unwrap(err)\n        log.Printf(\"watch died: %v\", cause)\n        watch.Close()\n        return\n    }\n    apply(events)\n}","preventionTips":["Always unwrap the cause under ErrWatchTerminated before choosing a fix","Close dead watches so subscriptions and handles are released","Fix environmental causes (limits, shares, permissions) before re-subscribing","Log backend causes: recurring patterns point to system-level issues"],"tags":["terminal","watch-lifecycle","error-wrapping","cross-platform"],"backgroundTag":null,"analyzedSha":"1bcfa18d79a3be41772223d5c05dfe4480e614ff","analyzedAt":"2026-08-16T02:12:00.115Z","schemaVersion":2},"datasetVersion":"2026-08-16T03:17:38.424Z"}