{"record":{"id":"1ddf5e136a7c5fa9","repo":"microsoft/typescript-go","slug":"fswatch-failed-to-watch-directory-q-w","errorCode":null,"errorMessage":"fswatch: failed to watch directory %q: %w","messagePattern":"fswatch: failed to watch directory %q: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/fswatch/watcher.go","lineNumber":284,"sourceCode":"\twatches, err := w.primary.WatchDirectories(requests)\n\tif err == nil || !errors.Is(err, ErrFilesystemUnsupported) {\n\t\treturn watches, err\n\t}\n\n\twatches = make([]Watch, 0, len(requests))\n\trollback := func() {\n\t\tfor i := len(watches) - 1; i >= 0; i-- {\n\t\t\t_ = watches[i].Close()\n\t\t}\n\t}\n\tfor _, request := range requests {\n\t\twatch, err := w.primary.WatchDirectory(request.Dir, request.Callback, request.Options...)\n\t\tif errors.Is(err, ErrFilesystemUnsupported) {\n\t\t\twatch, err = w.secondary.WatchDirectory(request.Dir, request.Callback, request.Options...)\n\t\t}\n\t\tif err != nil {\n\t\t\trollback()\n\t\t\treturn nil, fmt.Errorf(\"fswatch: failed to watch directory %q: %w\", request.Dir, err)\n\t\t}\n\t\twatches = append(watches, watch)\n\t}\n\treturn watches, nil\n}\n\nfunc (w *fallbackWatcher) WatchFile(path string, fn WatchCallback) (Watch, error) {\n\twatch, err := w.primary.WatchFile(path, fn)\n\tif errors.Is(err, ErrFilesystemUnsupported) {\n\t\treturn w.secondary.WatchFile(path, fn)\n\t}\n\treturn watch, err\n}\n\nfunc (w *fallbackWatcher) unexported() {}\n\n// watcher is the concrete implementation of [Watcher]. Each platform\n// watcher is a package-level *watcher whose factory is set by the","sourceCodeStart":266,"sourceCodeEnd":302,"githubUrl":"https://github.com/microsoft/typescript-go/blob/1bcfa18d79a3be41772223d5c05dfe4480e614ff/internal/fswatch/watcher.go#L266-L302","documentation":"Error from the fallback watcher's per-request re-subscribe path. When the primary (fanotify) batch fails with ErrFilesystemUnsupported, each request is retried individually; this error means that for request.Dir both the fanotify primary and the inotify secondary failed. The message names the directory and wraps the underlying error, and every watch already created for earlier requests in the batch is rolled back before returning.","triggerScenarios":"Fanotify() watching a filesystem it cannot handle (overlayfs, virtiofs, FUSE) where inotify then also fails: the directory disappeared, inotify instance or watch limits were hit (ENOSPC from fs.inotify.max_user_watches), or permissions deny the watch.","commonSituations":"Deep trees inside Docker containers where fanotify is unsupported and the inotify watch limit is also exceeded. Directories removed between the primary batch attempt and the secondary retry.","solutions":["Unwrap the error to identify the cause: ENOSPC means raise fs.inotify.max_user_watches and fs.inotify.max_user_instances via sysctl","Verify the directory exists at subscribe time; re-check and retry if it is created asynchronously","Trim recursive watch scope to reduce kernel watch consumption","Move the tree off the unsupported filesystem, or run outside the container"],"exampleFix":"// before\nwatches, err := fswatch.Fanotify().WatchDirectories(reqs)\nif err != nil { return err }\n\n// after\nwatches, err := fswatch.Fanotify().WatchDirectories(reqs)\nif err != nil {\n    var errno syscall.Errno\n    if errors.As(err, &errno) && errno == unix.ENOSPC {\n        // raise fs.inotify.max_user_watches, then retry\n    }\n    return err\n}","handlingStrategy":"try-catch","validationCode":"if _, err := os.Stat(dir); err != nil {\n    // create or wait for the directory before batch-subscribing\n}","typeGuard":null,"tryCatchPattern":"watches, err := fswatch.Fanotify().WatchDirectories(reqs)\nif err != nil {\n    var errno syscall.Errno\n    if errors.As(err, &errno) {\n        switch errno {\n        case unix.ENOSPC:\n            // raise fs.inotify.max_user_watches / max_user_instances, retry\n        case unix.EACCES:\n            // fix permissions on dir\n        case unix.ENOENT:\n            // recreate dir, retry\n        }\n    }\n}","preventionTips":["Raise inotify sysctl limits before watching large trees in containers","Verify directories exist before batch subscribe","Keep batches small enough that rollback is cheap","Unwrap batch errors: the fallback already retried; the secondary error is the real cause"],"tags":["linux","fanotify","inotify","fallback","resource-limits","docker"],"backgroundTag":null,"analyzedSha":"1bcfa18d79a3be41772223d5c05dfe4480e614ff","analyzedAt":"2026-08-16T02:12:00.115Z","schemaVersion":2},"datasetVersion":"2026-08-16T03:17:38.424Z"}