GoogleContainerTools/skaffold · error
pre-sync hooks failed for artifact %q: %w
Error message
pre-sync hooks failed for artifact %q: %w
What it means
The PodSyncer.Sync entry point runs the artifact's pre-sync lifecycle hooks (kubectl-based) before performing the copy/delete sync. If RunPreHooks fails, Skaffold wraps the error naming the artifact image so users know which artifact's hooks failed.
Source
Thrown at pkg/skaffold/sync/sync.go:295
if !item.HasChanges() {
return nil
}
var copy, delete []string
for k := range item.Copy {
copy = append(copy, k)
}
for k := range item.Delete {
delete = append(delete, k)
}
opts, err := hooks.NewSyncEnvOpts(item.Artifact, item.Image, copy, delete, *s.namespaces, s.kubectl.KubeContext)
if err != nil {
return err
}
hooksRunner := hooks.NewSyncRunner(s.kubectl, item.Artifact.ImageName, item.Image, *s.namespaces, s.formatter, item.Artifact.Sync.LifecycleHooks, opts)
if err := hooksRunner.RunPreHooks(ctx, out); err != nil {
return fmt.Errorf("pre-sync hooks failed for artifact %q: %w", item.Artifact.ImageName, err)
}
if err := s.sync(ctx, item); err != nil {
return err
}
if err := hooksRunner.RunPostHooks(ctx, out); err != nil {
return fmt.Errorf("post-sync hooks failed for artifact %q: %w", item.Artifact.ImageName, err)
}
return nil
}
func (s *PodSyncer) sync(ctx context.Context, item *Item) error {
if len(item.Copy) > 0 {
log.Entry(ctx).Info("Copying files:", item.Copy, "to", item.Image)
if err := Perform(ctx, item.Image, item.Copy, s.copyFileFn, *s.namespaces, s.kubectl.KubeContext); err != nil {
return fmt.Errorf("copying files: %w", err)
}
}View on GitHub (pinned to a1189de023)
Solutions
- Check the nested error's output: run the failing hook's command manually (kubectl exec) in the target pod/namespace
- Fix the preSync spec in skaffold.yaml (command, container name, timeouts) for the artifact named in the message
- Verify *s.namespaces and the kube context match where the app actually runs (kubectl config current-context)
Example fix
// before
sync:
lifecycleHooks:
preSync:
- exec: {command: ["./scripts/missing.sh"]}
// after
sync:
lifecycleHooks:
preSync:
- exec: {command: ["sh", "-c", "curl -fs http://localhost:8080/health"]} Defensive patterns
Strategy: try-catch
Validate before calling
for _, h := range a.Sync.LifecycleHooks.PreSync {
if h.Exec != nil && len(h.Exec.Command) == 0 { return errors.New("empty preSync exec command") }
if h.Container != nil && h.Container.ContainerName == "" { return errors.New("preSync container hook missing containerName") }
}
// also verify pod/namespace exist: kubectl get ns <ns> Try / catch
err := s.Sync(ctx, out, names, item)
if err != nil {
var hookErr interface{ Unwrap() error }
if errors.As(err, &hookErr) && strings.Contains(err.Error(), "pre-sync hooks failed") {
log.Errorf("artifact %s preSync failed, continuing sync state check: %v", item.Artifact.ImageName, err)
}
return err
} Prevention
- Test lifecycle hook commands manually in the target pod before adding them to skaffold.yaml
- Pin hook container names to ones guaranteed present in the pod spec
- Set explicit timeouts and make hooks idempotent so retries are safe
When it happens
Trigger: Any `sync.lifecycleHooks.preSync` exec or container hook returns non-zero, times out, or errors creating the hook environment (bad kube context, missing namespace, invalid hook spec) during a sync triggered by file change.
Common situations: preSync exec command references a binary absent in the container image; hook pod targets a namespace that doesn't exist; kube context switched to a cluster where the pod isn't running; container hook times out on slow startup.
Related errors
- post-sync hooks failed for artifact %q: %w
- failed to execute host %s hook %d for artifact %q: %w
- failed to execute container %s hook %d for artifact %q: %w
- copying files: %w
- kubectl version 1.12.0 or greater is recommended for use wit
AI-assisted analysis of GoogleContainerTools/skaffold@a1189de023 (2026-09-05).
Data as JSON: /api/errors/12f52c3da569087a.
Report an issue: GitHub.