docker/compose · error
can't watch with action %q on service %s without a command
Error message
can't watch with action %q on service %s without a command
What it means
A `develop.watch` trigger uses `action: sync_exec` (or `exec`) but its `exec:` block has no `command`. Compose validates that sync_exec rules carry a command to run after syncing, and fails config load when `trigger.Exec.Command` is empty.
Source
Thrown at pkg/compose/watch.go:428
for i, trigger := range config.Watch {
if !filepath.IsAbs(trigger.Path) {
trigger.Path = filepath.Join(baseDir, trigger.Path)
}
if p, err := filepath.EvalSymlinks(trigger.Path); err == nil {
// this might fail because the path doesn't exist, etc.
trigger.Path = p
}
trigger.Path = filepath.Clean(trigger.Path)
if trigger.Path == "" {
return nil, errors.New("watch rules MUST define a path")
}
if trigger.Action == types.WatchActionRebuild && service.Build == nil {
return nil, fmt.Errorf("service %s doesn't have a build section, can't apply %s on watch", types.WatchActionRebuild, service.Name)
}
if trigger.Action == types.WatchActionSyncExec && len(trigger.Exec.Command) == 0 {
return nil, fmt.Errorf("can't watch with action %q on service %s without a command", types.WatchActionSyncExec, service.Name)
}
config.Watch[i] = trigger
}
return &config, nil
}
func checkIfPathAlreadyBindMounted(watchPath string, volumes []types.ServiceVolumeConfig) bool {
for _, volume := range volumes {
if volume.Bind != nil {
relPath, err := filepath.Rel(volume.Source, watchPath)
if err == nil && !strings.HasPrefix(relPath, "..") {
return true
}
}
}
return false
}View on GitHub (pinned to ddc4b044b6)
Solutions
- Add an `exec: {command: [...]}` block to the sync_exec watch rule
- Use `action: sync` instead if no command should run after the copy
Example fix
# before
develop:
watch:
- path: ./src
action: sync_exec
target: /app/src
# after
develop:
watch:
- path: ./src
action: sync_exec
target: /app/src
exec:
command: make test Defensive patterns
Strategy: validation
Validate before calling
# assert every sync_exec rule declares a command
python3 - <<'EOF'
import yaml, sys
p = yaml.safe_load(open('compose.yaml'))
for name, svc in (p.get('services') or {}).items():
for rule in ((svc.get('develop') or {}).get('watch') or []):
if rule.get('action') in ('sync_exec', 'exec') and not (rule.get('exec') or {}).get('command'):
sys.exit(f"service {name}: sync_exec rule missing exec.command")
EOF Prevention
- Remember sync_exec = sync + required exec.command; plain sync needs none
- Validate the full file with `docker compose config` in CI to catch watch-rule mistakes early
When it happens
Trigger: Running `docker compose watch` / `up --watch` with a rule like `{path: ./src, action: sync_exec}` that omits `exec: {command: [...]}`. Raised in pkg/compose/watch.go during watch config validation.
Common situations: Renaming `sync` to `sync_exec` without adding an exec block; YAML indentation mistakes that leave `exec:` empty; misunderstanding that sync_exec requires a command while plain sync does not.
Related errors
- service %s doesn't have a build section, can't apply %s on w
- exit code %d
- process still running
- --wait cannot be combined with --abort-on-container-exit, --
- --detach cannot be combined with --abort-on-container-exit,
AI-assisted analysis of docker/compose@ddc4b044b6 (2026-08-15).
Data as JSON: /api/errors/767dd0b7d81c0468.
Report an issue: GitHub.