GoogleContainerTools/skaffold · error

failed to monitor files: %w

Error message

failed to monitor files: %w

What it means

skaffold's dev loop (`WatchForChanges`) starts a file monitor (`l.Monitor.Run`) that watches the workspace for file changes and feeds them into a debounced trigger. If the monitor fails on its very first run, the watch loop is aborted immediately with this wrapped error. It means skaffold could not establish the filesystem watcher, so change detection is impossible.

Source

Thrown at pkg/skaffold/runner/listen.go:69

}

func (l *SkaffoldListener) LogWatchToUser(out io.Writer) {
	l.Trigger.LogWatchToUser(out)
}

// WatchForChanges listens to a trigger, and when one is received, computes file changes and
// conditionally runs the dev loop.
func (l *SkaffoldListener) WatchForChanges(ctx context.Context, out io.Writer, devLoop func() error) error {
	ctxTrigger, cancelTrigger := context.WithCancel(ctx)
	defer cancelTrigger()
	trigger, err := trigger.StartTrigger(ctxTrigger, l.Trigger)
	if err != nil {
		return fmt.Errorf("unable to start trigger: %w", err)
	}

	// exit if file monitor fails the first time
	if err := l.Monitor.Run(l.Trigger.Debounce()); err != nil {
		return fmt.Errorf("failed to monitor files: %w", err)
	}

	l.LogWatchToUser(out)

	for {
		select {
		case <-ctx.Done():
			return nil
		case <-l.intentChan:
			if err := l.do(devLoop); err != nil {
				return err
			}
		case <-trigger:
			if err := l.do(devLoop); err != nil {
				return err
			}
		}
	}

View on GitHub (pinned to a1189de023)

Solutions

  1. Raise the inotify watch limit: `sudo sysctl fs.inotify.max_user_watches=524288` (and `fs.inotify.max_user_instances`).
  2. Verify the working directory exists and is readable; re-run skaffold from a valid checkout.
  3. Narrow the watched paths via `watch` config (`ignore`/`includes`) in skaffold.yaml to reduce descriptor usage.
  4. On network filesystems, run the source locally or use `--trigger=manual`/`polling` instead of the default watcher.
  5. Check `ulimit -n` (open file limit) and increase it if very low.

Example fix

// before
skaffold dev --watch-poll-interval=0  # watching entire $HOME, exhausting inotify
// after
# skaffold.yaml
watch:
  ignore:
    - "**/node_modules/**"
    - "**/.git/**"
Defensive patterns

Strategy: try-catch

Validate before calling

func canWatch(paths ...string) error {
  for _, p := range paths {
    if _, err := os.Stat(p); err != nil { return fmt.Errorf("watch path %q: %w", p, err) }
  }
  return nil
}

Try / catch

if err := runner.WatchForChanges(ctx, out); err != nil {
  if strings.Contains(err.Error(), "failed to monitor files") {
    log.Warn("file watcher unavailable (check inotify limits); falling back to polling")
    // retry with --trigger=polling or manual trigger
  }
  return err
}

Prevention

When it happens

Trigger: Running `skaffold dev` where `l.Monitor.Run(l.Trigger.Debounce())` returns an error on startup: the underlying watcher (fsnotify) fails to add watch descriptors, e.g. too many open files, watching a directory that was deleted, or watching a path the user cannot read.

Common situations: Dev loop over huge directory trees exhausting inotify watches (ulimit), watching a deleted/renamed working directory, permission-restricted folders, or network filesystems (NFS/SMB) that don't support inotify.

Related errors


AI-assisted analysis of GoogleContainerTools/skaffold@a1189de023 (2026-09-05). Data as JSON: /api/errors/7c7ac4378fd643e0. Report an issue: GitHub.