GoogleContainerTools/skaffold · error
preparing job %v for execution
Error message
preparing job %v for execution
What it means
K8sTask.Exec in Skaffold's k8sjob action runner calls k8sjobutil.ForceJobDelete to delete any stale Job with the same name before applying the new Job manifest. If that forced delete (or the preparation it performs) fails, the error is wrapped as "preparing job <name> for execution". The new Job cannot be scheduled until the old one is gone, since Job names are immutable in a namespace.
Source
Thrown at pkg/skaffold/actions/k8sjob/task.go:103
if err != nil {
return err
}
c := t.getContainerToDeploy()
var original corev1.Container
for _, ec := range t.jobManifest.Spec.Template.Spec.Containers {
if ec.Name == c.Name {
original = ec
break
}
}
patchToK8sContainer(c, &original)
t.jobManifest.Spec.Template.Spec.Containers = []corev1.Container{original}
t.jobManifest.ObjectMeta.Name = t.Name()
if err := k8sjobutil.ForceJobDelete(ctx, t.jobManifest.Name, jm, t.kubectl); err != nil {
return errors.Wrap(err, fmt.Sprintf("preparing job %v for execution", t.jobManifest.Name))
}
t.execEnv.TrackContainerAndJobFromBuild(graph.Artifact{
ImageName: t.jobManifest.Name,
Tag: t.jobManifest.Name,
}, tracker.Job{Name: t.jobManifest.Name, ID: t.jobManifest.Name}, &t.jobManifest)
if err := t.deployJob(ctx, t.jobManifest, jm); err != nil {
return err
}
if err = t.watchStatus(ctx, t.jobManifest, jm); err != nil {
t.execEnv.logger.CancelJobLogger(t.jobManifest.Name)
k8sjobutil.ForceJobDelete(context.TODO(), t.jobManifest.Name, jm, t.kubectl)
}
return err
}View on GitHub (pinned to a1189de023)
Solutions
- Check the unwrapped kubectl error; run `kubectl -n <ns> get job <name> -o yaml` and remove blocking finalizers if stuck Terminating.
- Manually delete the stale job (`kubectl -n <ns> delete job <name> --force --grace-period=0`) then rerun.
- Grant the active identity RBAC: `rules: [{apiGroups: [batch], resources: [jobs], verbs: ["delete", "create"]}]`.
- Verify kubeconfig context/namespace (kubectl config current-context) match the intended cluster.
- Retry if the API server was transiently unreachable.
Example fix
// before
if err := task.Exec(ctx, out); err != nil { return err }
// after
// pre-check stale job before Exec
err := exec.Command("kubectl", "-n", ns, "delete", "job", name, "--ignore-not-found").Run()
if err != nil { return fmt.Errorf("stale job cleanup failed: %w", err) }
if err := task.Exec(ctx, out); err != nil { return err } Defensive patterns
Strategy: retry
Validate before calling
# ensure stale job is gone before Exec kubectl -n $NS delete job $JOB --ignore-not-found kubectl -n $NS wait --for=delete job/$JOB --timeout=60s || true
Try / catch
// Go
err := task.Exec(ctx, out)
if err != nil && strings.Contains(err.Error(), "finalizer") {
// retry after unblocking finalizers / forced delete
err = task.Exec(ctx, out)
} Prevention
- Grant the runner RBAC delete on batch/jobs.
- Watch for jobs stuck Terminating (finalizers) and clean them.
- Verify kubeconfig context/namespace before runs.
When it happens
Trigger: Calling Exec when ForceJobDelete errors: a previous Job with the same name exists and cannot be deleted (finalizers blocking deletion, API server unreachable, RBAC forbids delete on jobs.batch, or bad kubecontext/namespace).
Common situations: A prior run's Job is stuck Terminating due to a finalizer; the service account running skaffold lacks delete permission on jobs; kubeconfig points at the wrong cluster/namespace; API server temporarily unavailable.
Related errors
- error in %v job execution, event type: %v
- error in %v job execution, job failed
- c.Message (pod status condition message)
- unable to lookup minikube executable. Please add it to PATH
- rs.ae.Message (actionable error message from status check)
AI-assisted analysis of GoogleContainerTools/skaffold@a1189de023 (2026-09-05).
Data as JSON: /api/errors/9046191f7e4782c5.
Report an issue: GitHub.