GoogleContainerTools/skaffold · error
failed to execute host %s hook %d for artifact %q: %w
Error message
failed to execute host %s hook %d for artifact %q: %w
What it means
During sync (or dev) pre/post phases, skaffold runs each host hook defined for an artifact. If a host (local shell) hook fails — and the failure is not a deliberate Skip — run wraps the original error with the phase, 1-based hook index, artifact image name, and underlying error, then aborts the sync.
Source
Thrown at pkg/skaffold/hooks/sync.go:86
return r.run(ctx, out, r.PostHooks, phases.PostSync)
}
func (r syncRunner) getEnv() []string {
common := getEnv(staticEnvOpts)
sync := getEnv(r.opts)
return append(common, sync...)
}
func (r syncRunner) run(ctx context.Context, out io.Writer, hooks []latest.SyncHookItem, phase phase) error {
if len(hooks) > 0 {
output.Default.Fprintf(out, "Starting %s hooks for artifact %q...\n", phase, r.imageName)
}
env := r.getEnv()
for i, h := range hooks {
if h.HostHook != nil {
hook := hostHook{*h.HostHook, env}
if err := hook.run(ctx, nil, out); err != nil && !errors.Is(err, &Skip{}) {
return fmt.Errorf("failed to execute host %s hook %d for artifact %q: %w", phase, i+1, r.imageName, err)
}
} else if h.ContainerHook != nil {
hook := containerHook{
cfg: *h.ContainerHook,
cli: r.cli,
selector: runningImageSelector(r.imageRef),
namespaces: r.namespaces,
formatter: r.formatter,
}
if err := hook.run(ctx, out); err != nil {
return fmt.Errorf("failed to execute container %s hook %d for artifact %q: %w", phase, i+1, r.imageName, err)
}
}
}
if len(hooks) > 0 {
output.Default.Fprintf(out, "Completed %s hooks for artifact %q\n", phase, r.imageName)
}
return nilView on GitHub (pinned to a1189de023)
Solutions
- Read the wrapped %w error in the message — it names the failing command and exit reason; fix that command
- Test the hook command manually with the same working directory skaffold uses (the build workspace)
- Make the hook executable (chmod +x) or use an explicit interpreter prefix (sh -c ...)
- If the hook may legitimately fail, handle the Skip sentinel or make the script exit 0 with a warning
Example fix
// before (skaffold.yaml)
sync:
hooks:
before:
- host:
command: ["./scripts/migrate"]
// after
sync:
hooks:
before:
- host:
command: ["sh", "-c", "./scripts/migrate || echo 'migration skipped' >&2"] Defensive patterns
Strategy: try-catch
Validate before calling
// pre-check host hooks before enabling them in sync:
for _, cmd := range hookCmds {
if err := exec.Command("sh", "-c", "command -v "+cmd[0]).Run(); err != nil {
return fmt.Errorf("hook command %q not available", cmd[0])
}
} Try / catch
if err := syncRunner.RunPreHooks(ctx, out); err != nil {
var skip *hooks.Skip
if !errors.As(err, &skip) {
log.Printf("host hook failed: %v", err) // wrapped msg names phase, hook index, artifact
}
} Prevention
- Test each host hook manually from the build workspace before adding it to config
- Use sh -c with explicit interpreter paths; add scripts to the repo with +x
- Keep host hooks idempotent and fast so failures are easy to retry
When it happens
Trigger: A hostHook command in skaffold.yaml (e.g. `before-hooks`/`after-hooks` under sync) exits non-zero, times out, or the executable isn't found while RunPreHooks/RunPostHooks iterate hooks for artifact r.imageName.
Common situations: Hook script missing execute bit or not on PATH; command exits non-zero due to a failing migration or script bug; shell interpreter missing in minimal environments; intentional failure during tests.
Related errors
- failed to execute container %s hook %d for artifact %q: %w
- pre-sync hooks failed for artifact %q: %w
- post-sync hooks failed for artifact %q: %w
- the length of stdout should be greater than 0 when using ren
- determining build workspace directory for image %v: %w
AI-assisted analysis of GoogleContainerTools/skaffold@a1189de023 (2026-09-05).
Data as JSON: /api/errors/ea942105188ee350.
Report an issue: GitHub.