docker/compose · error

creating file watcher: %w

Error message

creating file watcher: %w

What it means

The generic counterpart to the inotify-limit case: fsnotify.NewWatcher() failed for any reason other than the detected 'too many open files' pattern, or the platform is not Linux. The underlying error is wrapped with creating file watcher so the cause survives. Typical causes are fd exhaustion (ulimit -n), EMFILE variants with different wording, or unsupported platforms for the backend.

Source

Thrown at pkg/watch/watcher_naive.go:308

func (d *naiveNotify) add(path string) error {
	err := d.watcher.Add(path)
	if err != nil {
		return err
	}
	d.numWatches++
	numberOfWatches.Add(1)
	return nil
}

func newWatcher(paths []string) (Notify, error) {
	fsw, err := fsnotify.NewWatcher()
	if err != nil {
		if strings.Contains(err.Error(), "too many open files") && runtime.GOOS == "linux" {
			return nil, fmt.Errorf("hit OS limits creating a watcher.\n" +
				"Run 'sysctl fs.inotify.max_user_instances' to check your inotify limits.\n" +
				"To raise them, run 'sudo sysctl fs.inotify.max_user_instances=1024'")
		}
		return nil, fmt.Errorf("creating file watcher: %w", err)
	}
	MaybeIncreaseBufferSize(fsw)

	err = fsw.SetRecursive()
	isWatcherRecursive := err == nil

	wrappedEvents := make(chan FileEvent)
	notifyList := make(map[string]bool, len(paths))
	if isWatcherRecursive {
		paths = pathutil.EncompassingPaths(paths)
	}
	for _, path := range paths {
		path, err := filepath.Abs(path)
		if err != nil {
			return nil, fmt.Errorf("newWatcher: %w", err)
		}
		notifyList[path] = true
	}

View on GitHub (pinned to ddc4b044b6)

Solutions

  1. Check the wrapped cause: EMFILE means raise the process fd limit (`ulimit -n 4096` or LimitNOFILE in systemd); ENFILE means the whole system is out of fds — find the fd hog with `lsof | awk '{print $1}' | sort | uniq -c | sort -rn`.
  2. Verify inotify is permitted: seccomp/cap policies in containers must allow inotify_init1 (try unconfined or adjust the profile).
  3. Reduce concurrent watchers or restart leaking processes to free descriptors.

Example fix

# before: default soft limit causes EMFILE
ulimit -n   # 1024
# after
ulimit -n 4096
docker compose watch
Defensive patterns

Strategy: try-catch

Validate before calling

// check fd headroom before creating watchers
var rlim syscall.Rlimit
if err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &rlim); err == nil {
    if countOpenFds() > int(rlim.Cur)-64 {
        return fmt.Errorf("raise ulimit -n before starting file watchers")
    }
}

Try / catch

if err != nil {
    var errno syscall.Errno
    if errors.As(err, &errno) && (errno == syscall.EMFILE || errno == syscall.ENFILE) {
        // EMFILE: raise process fd limit (ulimit -n / LimitNOFILE)
        // ENFILE: system-wide exhaustion — identify fd hogs with lsof, no local retry helps
    }
    return err
}

Prevention

When it happens

Trigger: fsnotify.NewWatcher() returning an error on a non-Linux OS, or a Linux error string not matching 'too many open files' (e.g. localized messages or 'too many open files in system' variants hitting ENFILE — a system-wide fd limit).

Common situations: Process fd limit (ulimit -n) exhausted by leaked file descriptors in the app or plugins; ENFILE system-wide fd exhaustion on busy hosts; running on an OS where inotify is unavailable (some minimal containers/seccomp profiles block inotify_init).

Related errors


AI-assisted analysis of docker/compose@ddc4b044b6 (2026-08-15). Data as JSON: /api/errors/2bd29060d8445318. Report an issue: GitHub.