{"record":{"id":"75471aaf9c28e967","repo":"docker/compose","slug":"watcher-add-q-w","errorCode":null,"errorMessage":"watcher.Add(%q): %w","messagePattern":"watcher\\.Add\\(%q\\): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/watch/watcher_naive.go","lineNumber":115,"sourceCode":"\t\t\terr = d.addWatch(filepath.Dir(name))\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"notify.Add(%q): %w\", filepath.Dir(name), err)\n\t\t\t}\n\t\t}\n\t}\n\n\tgo d.loop()\n\n\treturn nil\n}\n\nfunc (d *naiveNotify) watchRecursively(dir string) error {\n\tif d.isWatcherRecursive {\n\t\terr := d.addWatch(dir)\n\t\tif err == nil || os.IsNotExist(err) {\n\t\t\treturn nil\n\t\t}\n\t\treturn fmt.Errorf(\"watcher.Add(%q): %w\", dir, err)\n\t}\n\n\treturn filepath.WalkDir(dir, d.walkAndAdd)\n}\n\n// walkAndAdd puts a watch on every directory of the tree being walked.\nfunc (d *naiveNotify) walkAndAdd(path string, info fs.DirEntry, err error) error {\n\tif err != nil {\n\t\t// A directory we are not allowed to read is not a reason to abandon the\n\t\t// whole watch: we simply cannot see inside it, so skip it and carry on.\n\t\tif os.IsPermission(err) {\n\t\t\tlogrus.Debugf(\"Not watching %s: %v\", path, err)\n\t\t\treturn filepath.SkipDir\n\t\t}\n\t\treturn err\n\t}\n\n\tif !info.IsDir() {","sourceCodeStart":97,"sourceCodeEnd":133,"githubUrl":"https://github.com/docker/compose/blob/ddc4b044b62e9f715212ea4143fa830fac76382f/pkg/watch/watcher_naive.go#L97-L133","documentation":"watchRecursively has a fast path: when the fsnotify watcher supports recursion (SetRecursive succeeded), one addWatch(dir) covers the whole tree; IsNotExist errors are tolerated (raced deletions) and everything else is wrapped as watcher.Add. The failure means the kernel refused the single recursive watch.","triggerScenarios":"d.Add on a directory while running on a backend where isWatcherRecursive is true, and addWatch returns an error other than IsNotExist — e.g. inotify instance limit (EMFILE) on Linux recursive fsnotify, fanotify permission issues, or the directory disappearing concurrently with a non-NotExist errno.","commonSituations":"Hitting fs.inotify.max_user_instances because many compose/dev sessions each hold watchers; running inside containers where fanotify/inotify recursive support requires privileges the process lacks; kernel version differences changing which errors surface.","solutions":["Check and raise instance limits: `sysctl fs.inotify.max_user_instances` then `sudo sysctl fs.inotify.max_user_instances=1024`.","Close other watch sessions (VS Code remote, other compose watch runs) to free instances.","If in a container, ensure the container has the capabilities/sysctls needed for inotify/fanotify, or run the watcher on the host."],"exampleFix":"# before: default limits, many watchers\nsysctl fs.inotify.max_user_instances   # e.g. 128\n# after\nsudo sysctl fs.inotify.max_user_instances=1024","handlingStrategy":"try-catch","validationCode":"// Linux: check instance headroom before creating watchers\nif runtime.GOOS == \"linux\" {\n    b, _ := os.ReadFile(\"/proc/sys/fs/inotify/max_user_instances\")\n    // compare against open inotify fds: len(glob(\"/proc/self/fd/*\")) heuristics\n}","typeGuard":null,"tryCatchPattern":"if err != nil {\n    if errors.Is(err, syscall.EMFILE) {\n        // actionable: suggest sysctl fs.inotify.max_user_instances=1024, do not retry\n    }\n    return err\n}","preventionTips":["Raise fs.inotify.max_user_instances on Linux hosts using recursive watchers.","Close stale compose watch sessions and editor remote servers to free instances.","In containers, ensure the runtime permits inotify/fanotify initialization."],"tags":["go","docker-compose","watch","inotify","linux","resource-limits"],"backgroundTag":null,"analyzedSha":"ddc4b044b62e9f715212ea4143fa830fac76382f","analyzedAt":"2026-08-15T13:31:42.319Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}