GoogleContainerTools/skaffold · error · CleanupErr

strings.Join(errMsgs, "\n") (joined helm cleanup error messa

Error message

strings.Join(errMsgs, "\n") (joined helm cleanup error messages)

What it means

Helm deployer Cleanup runs cleanup commands (e.g. helm delete/hooks) for each release; if any sub-command fails, all messages are collected into errMsgs and joined with newlines, then wrapped via deployerr.CleanupErr. The resulting error text is a multi-line aggregation of every helm failure encountered during cleanup.

Source

Thrown at pkg/skaffold/deploy/helm/helm.go:480

		}
		args := []string{}
		if dryRun {
			args = append(args, "get", "manifest")
		} else {
			args = append(args, "delete")
		}
		args = append(args, releaseName)

		if namespace != "" {
			args = append(args, "--namespace", namespace)
		}
		if err := helm.Exec(ctx, h, out, false, nil, args...); err != nil {
			errMsgs = append(errMsgs, err.Error())
		}
	}

	if len(errMsgs) != 0 {
		return deployerr.CleanupErr(errors.New(strings.Join(errMsgs, "\n")))
	}
	return nil
}

func (h *Deployer) HasRunnableHooks() bool {
	return len(h.LegacyHelmDeploy.LifecycleHooks.PreHooks) > 0 || len(h.LegacyHelmDeploy.LifecycleHooks.PostHooks) > 0
}

func (h *Deployer) PreDeployHooks(ctx context.Context, out io.Writer) error {
	childCtx, endTrace := instrumentation.StartTrace(ctx, "Deploy_PreHooks")
	if err := h.hookRunner.RunPreHooks(childCtx, out); err != nil {
		endTrace(instrumentation.TraceEndError(err))
		return err
	}
	endTrace()
	return nil
}

View on GitHub (pinned to a1189de023)

Solutions

  1. Read each line of the joined message to see which release/step failed
  2. If the release is already gone, treat it as done or delete the release entry from state; rerun with the release name removed from skaffold.yaml
  3. Re-check cluster connectivity and kubeconfig credentials (kubectl get ns) then retry cleanup
  4. Run the failing helm command manually with --debug to get the root cause

Example fix

// before (release already deleted, cleanup fails)
skaffold delete
// after (skip already-deleted release or clean state)
helm list -n mynamespace  # confirm release absent
skaffold delete --cue-... # or remove stale release from deploy state
# alternatively: kubectl delete job/servicemanual leftovers manually
Defensive patterns

Strategy: try-catch

Validate before calling

// before cleanup, check the release exists:
rel := "<release>"
out, err := exec.Command("helm", "status", rel, "-n", ns).CombinedOutput()
releaseExists := err == nil

Try / catch

err := d.Cleanup(ctx, out)
if err != nil {
    for _, msg := range strings.Split(err.Error(), "\n") {
        log.Printf("helm cleanup failure: %s", msg)
        if strings.Contains(msg, "release: not found") {
            log.Println("release already removed; treating as cleaned up")
        }
    }
}

Prevention

When it happens

Trigger: Calling Cleanup on a Helm deployer where one or more helm Exec invocations (e.g. 'helm delete' for a release, or hook executions) return non-zero exit codes.

Common situations: Release already deleted manually via helm CLI so 'helm delete' fails with 'release: not found'; cluster unreachable or credentials expired; helm hooks failing during uninstall; RBAC restrictions preventing deletion.

Related errors


AI-assisted analysis of GoogleContainerTools/skaffold@a1189de023 (2026-09-05). Data as JSON: /api/errors/5a8a84b37d7aaff7. Report an issue: GitHub.