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
- Raise the inotify watch limit: `sudo sysctl fs.inotify.max_user_watches=524288` (and `fs.inotify.max_user_instances`).
- Verify the working directory exists and is readable; re-run skaffold from a valid checkout.
- Narrow the watched paths via `watch` config (`ignore`/`includes`) in skaffold.yaml to reduce descriptor usage.
- On network filesystems, run the source locally or use `--trigger=manual`/`polling` instead of the default watcher.
- 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
- Raise fs.inotify.max_user_watches/max_user_instances on dev machines and CI images.
- Configure `watch.ignore` for node_modules, .git, and vendor trees.
- Avoid watching network mounts; keep source on a local filesystem.
- Check working directory validity before starting dev loops in scripts.
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
- no receiver was registered
- configuration changed
- errStr (aggregated event log file errors, e.g. "eventV2 log
- didn't sync any files
- writing build output to file: %w
AI-assisted analysis of GoogleContainerTools/skaffold@a1189de023 (2026-09-05).
Data as JSON: /api/errors/7c7ac4378fd643e0.
Report an issue: GitHub.