{"record":{"id":"492a7e9a3c6aa6b5","repo":"helm/helm","slug":"an-error-occurred-while-cleaning-up-resources-ori","errorCode":null,"errorMessage":"an error occurred while cleaning up resources. original rollback error: %w","messagePattern":"an error occurred while cleaning up resources\\. original rollback error: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"pkg/action/rollback.go","lineNumber":264,"sourceCode":"\t\tcurrent,\n\t\ttarget,\n\t\tkube.ClientUpdateOptionForceReplace(r.ForceReplace),\n\t\tkube.ClientUpdateOptionServerSideApply(serverSideApply, r.ForceConflicts),\n\t\tkube.ClientUpdateOptionThreeWayMergeForUnstructured(false),\n\t\tkube.ClientUpdateOptionUpgradeClientSideFieldManager(true))\n\tif err != nil {\n\t\tmsg := fmt.Sprintf(\"Rollback %q failed: %s\", targetRelease.Name, err)\n\t\tr.cfg.Logger().Warn(msg)\n\t\tcurrentRelease.Info.Status = common.StatusSuperseded\n\t\ttargetRelease.Info.Status = common.StatusFailed\n\t\ttargetRelease.Info.Description = msg\n\t\tr.cfg.recordRelease(currentRelease)\n\t\tr.cfg.recordRelease(targetRelease)\n\t\tif r.CleanupOnFail {\n\t\t\tr.cfg.Logger().Debug(\"cleanup on fail set, cleaning up resources\", \"count\", len(results.Created))\n\t\t\t_, errs := r.cfg.KubeClient.Delete(results.Created, metav1.DeletePropagationBackground)\n\t\t\tif errs != nil {\n\t\t\t\treturn targetRelease, fmt.Errorf(\n\t\t\t\t\t\"an error occurred while cleaning up resources. original rollback error: %w\",\n\t\t\t\t\tfmt.Errorf(\"unable to cleanup resources: %w\", joinErrors(errs, \", \")))\n\t\t\t}\n\t\t\tr.cfg.Logger().Debug(\"resource cleanup complete\")\n\t\t}\n\t\treturn targetRelease, err\n\t}\n\n\tvar waiter kube.Waiter\n\tif c, supportsOptions := r.cfg.KubeClient.(kube.InterfaceWaitOptions); supportsOptions {\n\t\twaiter, err = c.GetWaiterWithOptions(r.WaitStrategy, r.WaitOptions...)\n\t} else {\n\t\twaiter, err = r.cfg.KubeClient.GetWaiter(r.WaitStrategy)\n\t}\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"unable to get waiter: %w\", err)\n\t}\n\tif r.WaitForJobs {","sourceCodeStart":246,"sourceCodeEnd":282,"githubUrl":"https://github.com/helm/helm/blob/2a29f1770b62844b27197d2507377361d45ad7c0/pkg/action/rollback.go#L246-L282","documentation":"Thrown by Rollback.performUpgrade when a rollback's Kubernetes Update() call already failed (release marked failed/superseded), the user enabled CleanupOnFail, and the follow-up attempt to delete the newly created resources (results.Created) also failed. It wraps the original rollback error as cause with the cleanup failure nested inside, so nothing is silently lost. It means the cluster is left in a partially rolled-back state that Helm cannot clean up automatically.","triggerScenarios":"helm rollback with --cleanup-on-fail where KubeClient.Update returns an error after creating some resources, and the subsequent KubeClient.Delete(results.Created, DeletePropagationBackground) returns at least one error. Programmatic equivalent: Rollback action with CleanupOnFail=true, a failing cluster update, then failing deletes (RBAC denial on delete, resource already being garbage-collected, API timeout).","commonSituations":"ServiceAccount lacks delete permissions but has create/update; cluster admission controllers (e.g. finalizer-adding webhooks) block deletion; network disruption to the API server mid-rollback; resources created with ownerReferences that propagate deletes unexpectedly; concurrent operator fighting the rollback.","solutions":["Inspect the wrapped chain: the outer message names cleanup, the inner 'unable to cleanup resources' lists per-resource delete errors, and %w carries the original rollback error — fix that root cause first","Manually delete the leftover created resources with kubectl (match what the rollback had just created; check release manifest and events)","Verify RBAC: the identity needs delete on every resource kind the chart creates, not just create/update","Retry the rollback after the root cause is fixed; check 'helm history' and 'kubectl get events' to confirm the failed/created set","If a webhook/finalizer blocks deletes, remove the finalizer or exempt the rollback identity before retrying"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"res, err := rollback.RunWithContext(ctx, name)\nif err != nil {\n    if strings.Contains(err.Error(), \"an error occurred while cleaning up resources\") {\n        // release marked failed in storage; cluster partially rolled back\n        var cleanupInner *myJoinErr // unwrap chain: outer %w -> inner \"unable to cleanup resources\"\n        if inner := errors.Unwrap(errors.Unwrap(err)); inner != nil {\n            log.Printf(\"per-resource delete failures: %v\", inner)\n        }\n        // audit created-but-not-deleted resources before retrying\n    }\n    return err\n}","preventionTips":["Only set CleanupOnFail when the identity also holds delete on all chart kinds","Run rollback in automation that snapshots kubectl get for the release's resources before/after, so partial cleanup is detectable","Avoid admission webhooks that add finalizers blocking deletion of chart resources","Serialize rollbacks per release to prevent concurrent delete races"],"tags":["helm","rollback","kubernetes","cleanup","rbac","partial-state"],"backgroundTag":null,"analyzedSha":"2a29f1770b62844b27197d2507377361d45ad7c0","analyzedAt":"2026-08-15T22:02:47.490Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}