docker/compose · error
watcher.Add(%q): %w
Error message
watcher.Add(%q): %w
What it means
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.
Source
Thrown at pkg/watch/watcher_naive.go:115
err = d.addWatch(filepath.Dir(name))
if err != nil {
return fmt.Errorf("notify.Add(%q): %w", filepath.Dir(name), err)
}
}
}
go d.loop()
return nil
}
func (d *naiveNotify) watchRecursively(dir string) error {
if d.isWatcherRecursive {
err := d.addWatch(dir)
if err == nil || os.IsNotExist(err) {
return nil
}
return fmt.Errorf("watcher.Add(%q): %w", dir, err)
}
return filepath.WalkDir(dir, d.walkAndAdd)
}
// walkAndAdd puts a watch on every directory of the tree being walked.
func (d *naiveNotify) walkAndAdd(path string, info fs.DirEntry, err error) error {
if err != nil {
// A directory we are not allowed to read is not a reason to abandon the
// whole watch: we simply cannot see inside it, so skip it and carry on.
if os.IsPermission(err) {
logrus.Debugf("Not watching %s: %v", path, err)
return filepath.SkipDir
}
return err
}
if !info.IsDir() {View on GitHub (pinned to ddc4b044b6)
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.
Example fix
# before: default limits, many watchers sysctl fs.inotify.max_user_instances # e.g. 128 # after sudo sysctl fs.inotify.max_user_instances=1024
Defensive patterns
Strategy: try-catch
Validate before calling
// Linux: check instance headroom before creating watchers
if runtime.GOOS == "linux" {
b, _ := os.ReadFile("/proc/sys/fs/inotify/max_user_instances")
// compare against open inotify fds: len(glob("/proc/self/fd/*")) heuristics
} Try / catch
if err != nil {
if errors.Is(err, syscall.EMFILE) {
// actionable: suggest sysctl fs.inotify.max_user_instances=1024, do not retry
}
return err
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- hit OS limits creating a watcher. Run 'sysctl fs.inotify.max
- notify.Add(%q): %w
- newWatcher: %w
- error reading .dockerignore: %w
- cannot watch root directory
AI-assisted analysis of docker/compose@ddc4b044b6 (2026-08-15).
Data as JSON: /api/errors/75471aaf9c28e967.
Report an issue: GitHub.