GoogleContainerTools/skaffold · error
unable to start trigger: %w
Error message
unable to start trigger: %w
What it means
SkaffoldListener.WatchForChanges starts the change trigger (manual/polling/notify) that gates the dev loop. If trigger.StartTrigger fails, the listener returns 'unable to start trigger'. The trigger is what decides when the dev loop re-runs (file events, polling interval, or manual Enter).
Source
Thrown at pkg/skaffold/runner/listen.go:64
type SkaffoldListener struct {
Monitor filemon.Monitor
Trigger trigger.Trigger
sourceDependenciesCache graph.SourceDependenciesCache
intentChan <-chan bool
}
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:View on GitHub (pinned to a1189de023)
Solutions
- Fall back to polling: run with `--trigger=poll` (and a sane `--poll-interval`, e.g. 1s)
- Fix any invalid trigger flags (e.g. non-numeric --poll-interval)
- Raise inotify limits if watcher creation fails (`fs.inotify.max_user_watches/instances`)
- Run with --trigger=manual to isolate whether the trigger subsystem is the failure
Example fix
// before $ skaffold dev # notify trigger fails on network FS // after $ skaffold dev --trigger=poll --poll-interval=1000
Defensive patterns
Strategy: fallback
Validate before calling
// prefer polling trigger on filesystems without notify support
trigger := "notify"
if onNetworkFilesystem(cwd) { trigger = "poll" }
args := []string{"dev", "--trigger=" + trigger, "--poll-interval=1000"} Type guard
func isTriggerStartErr(err error) bool {
return err != nil && strings.Contains(err.Error(), "unable to start trigger")
} Try / catch
if err := listener.WatchForChanges(ctx, out, devLoop); isTriggerStartErr(err) {
log.Println("notify trigger failed; retrying with poll trigger")
l.Trigger = pollingTrigger
return listener.WatchForChanges(ctx, out, devLoop)
} Prevention
- Use --trigger=poll on NFS/network mounts and in restricted containers
- Validate --poll-interval values before launching
- Raise inotify instance limits for notify mode
- Test trigger startup with --trigger=manual to isolate the subsystem
When it happens
Trigger: `trigger.StartTrigger(ctxTrigger, l.Trigger)` returns an error — e.g. the polling trigger cannot initialize its poller, the notify trigger cannot create a file watcher, or trigger configuration (interval, mode) is invalid.
Common situations: Filesystem doesn't support notify-based watching (network mounts, containers with limited syscalls) while --trigger=notify is default; invalid --poll-interval value; watch resource exhaustion when creating the trigger's watcher.
Related errors
- watching files for artifact %q: %w
- watching test files: %w
- watching skaffold configuration %q: %w
- creating watch trigger: %w
- didn't sync any files
AI-assisted analysis of GoogleContainerTools/skaffold@a1189de023 (2026-09-05).
Data as JSON: /api/errors/eeae80698fba091b.
Report an issue: GitHub.