{"record":{"id":"644e8a877a138942","repo":"helm/helm","slug":"unable-to-get-waiter-w-644e8a","errorCode":null,"errorMessage":"unable to get waiter: %w","messagePattern":"unable to get waiter: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/action/rollback.go","lineNumber":280,"sourceCode":"\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 {\n\t\tif err := waiter.WaitWithJobs(target, r.Timeout); err != nil {\n\t\t\ttargetRelease.SetStatus(common.StatusFailed, fmt.Sprintf(\"Release %q failed: %s\", targetRelease.Name, err.Error()))\n\t\t\tr.cfg.recordRelease(currentRelease)\n\t\t\tr.cfg.recordRelease(targetRelease)\n\t\t\treturn targetRelease, fmt.Errorf(\"release %s failed: %w\", targetRelease.Name, err)\n\t\t}\n\t} else {\n\t\tif err := waiter.Wait(target, r.Timeout); err != nil {\n\t\t\ttargetRelease.SetStatus(common.StatusFailed, fmt.Sprintf(\"Release %q failed: %s\", targetRelease.Name, err.Error()))\n\t\t\tr.cfg.recordRelease(currentRelease)\n\t\t\tr.cfg.recordRelease(targetRelease)\n\t\t\treturn targetRelease, fmt.Errorf(\"release %s failed: %w\", targetRelease.Name, err)\n\t\t}\n\t}\n\n\t// post-rollback hooks","sourceCodeStart":262,"sourceCodeEnd":298,"githubUrl":"https://github.com/helm/helm/blob/2a29f1770b62844b27197d2507377361d45ad7c0/pkg/action/rollback.go#L262-L298","documentation":"Returned by Rollback.performRollback when constructing the waiter fails: the kube client's GetWaiterWithOptions(WaitStrategy, WaitOptions...) (when it implements kube.InterfaceWaitOptions) or GetWaiter(WaitStrategy) returns an error. The cluster resources were already updated; only the post-apply wait phase could not start. Note it returns a nil release alongside the error, unlike neighbouring failures which return targetRelease.","triggerScenarios":"Passing a WaitStrategy the configured kube client does not support (e.g. a strategy name not registered in the client's wait strategy set), or wait options rejected by GetWaiterWithOptions (invalid combination such as incompatible strategy+options).","commonSituations":"SDK users constructing Configuration with a custom/mock KubeClient that does not implement InterfaceWaitOptions or implements it with a limited strategy registry; mismatched WaitStrategy constants between helm versions; typo'd or version-drifted strategy passed via WaitOptions.","solutions":["Use strategy constants from the same helm version's kube package (e.g. kube.StatusWatcherStrategy) rather than string literals","If injecting a custom KubeClient, implement GetWaiter for every strategy you pass, or implement InterfaceWaitOptions and accept/return a waiter for the strategies your code uses","Log the wrapped error — it names the unsupported strategy or option","Fall back to the default wait strategy (leave WaitStrategy zero / use the client default) to confirm the rest of the rollback works"],"exampleFix":"// before: custom client breaks waiter resolution\nrollback := action.NewRollback(cfg)\nrollback.WaitStrategy = kube.WaitStrategy(\"bogus\")\n_, err := rollback.RunWithContext(ctx, \"my-release\")\n\n// after: use a supported constant from the same kube package\nrollback.WaitStrategy = kube.StatusWatcherStrategy","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"// probe the client's waiter surface before running the rollback\nvar waiter kube.Waiter\nif c, ok := cfg.KubeClient.(kube.InterfaceWaitOptions); ok {\n    waiter, err = c.GetWaiterWithOptions(strategy, opts...)\n} else {\n    waiter, err = cfg.KubeClient.GetWaiter(strategy)\n}\nif err != nil {\n    return fmt.Errorf(\"wait strategy unsupported by client, pick another: %w\", err)\n}","tryCatchPattern":"if err != nil {\n    if strings.Contains(err.Error(), \"unable to get waiter\") {\n        // resources WERE applied; only the wait phase aborted.\n        // verify resource state manually instead of blindly retrying the rollback.\n        return verifyRollbackStateManually(ctx, targetRelease)\n    }\n}","preventionTips":["Pin WaitStrategy to constants from the same helm/kube package version you link","With custom KubeClients, implement GetWaiter for every strategy your code passes and add a unit test asserting it","Smoke-test rollback (not just install) in CI when you customize the kube client"],"tags":["helm","rollback","wait","strategy","sdk"],"backgroundTag":null,"analyzedSha":"2a29f1770b62844b27197d2507377361d45ad7c0","analyzedAt":"2026-08-15T22:02:47.490Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}