{"record":{"id":"13da25648342a4ec","repo":"derailed/k9s","slug":"user-is-not-authorized-to-patch-a-statefulset","errorCode":null,"errorMessage":"user is not authorized to patch a statefulset","messagePattern":"user is not authorized to patch a statefulset","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/dao/sts.go","lineNumber":230,"sourceCode":"// GetPodSpec returns a pod spec given a resource.\nfunc (s *StatefulSet) GetPodSpec(path string) (*v1.PodSpec, error) {\n\tsts, err := s.getStatefulSet(path)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tpodSpec := sts.Spec.Template.Spec\n\treturn &podSpec, nil\n}\n\n// SetImages sets container images.\nfunc (s *StatefulSet) SetImages(ctx context.Context, path string, imageSpecs ImageSpecs) error {\n\tns, n := client.Namespaced(path)\n\tauth, err := s.Client().CanI(ns, client.StsGVR, n, client.PatchAccess)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif !auth {\n\t\treturn fmt.Errorf(\"user is not authorized to patch a statefulset\")\n\t}\n\tjsonPatch, err := GetTemplateJsonPatch(imageSpecs)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdial, err := s.Client().Dial()\n\tif err != nil {\n\t\treturn err\n\t}\n\t_, err = dial.AppsV1().StatefulSets(ns).Patch(\n\t\tctx,\n\t\tn,\n\t\ttypes.StrategicMergePatchType,\n\t\tjsonPatch,\n\t\tmetav1.PatchOptions{},\n\t)\n\treturn err\n}","sourceCodeStart":212,"sourceCodeEnd":248,"githubUrl":"https://github.com/derailed/k9s/blob/2d3ccc6ba2ce98c3781bfc441bb3e884f072774f/internal/dao/sts.go#L212-L248","documentation":"StatefulSet.SetImages first runs a SelfSubjectAccessReview (Client().CanI) for the patch verb on apps/statefulsets before issuing the patch. If the review returns not-allowed, the image update is refused client-side with this error and no request reaches the API server.","triggerScenarios":"Calling SetImages (e.g. using the image-swap/edit-image action on a StatefulSet in k9s) with a kubeconfig user bound to a Role/ClusterRole lacking the patch verb on statefulsets.apps in that namespace.","commonSituations":"Read-only viewer accounts, CI service accounts, or restricted RBAC in shared clusters; assuming a different (under-privileged) user via k9s impersonation flags; kubeconfig contexts pointing at a token with minimal scopes.","solutions":["Confirm the gap: kubectl auth can-i patch statefulsets.apps -n <ns> (as the same user)","Grant the verb: add resources: [statefulsets], verbs: [patch] (or [patch, get]) to the Role/ClusterRole bound to the user","Rebind the role with a RoleBinding in that namespace, or switch to a context/user that already has patch rights","If you cannot get patch rights, update images by re-applying the manifest with kubectl apply/set image from an authorized account"],"exampleFix":"# before: role without patch\napiVersion: rbac.authorization.k8s.io/v1\nkind: Role\nmetadata:\n  name: app-dev\nrules:\n- apiGroups: [\"apps\"]\n  resources: [\"statefulsets\"]\n  verbs: [\"get\", \"list\"]\n# after: patch granted\napiVersion: rbac.authorization.k8s.io/v1\nkind: Role\nmetadata:\n  name: app-dev\nrules:\n- apiGroups: [\"apps\"]\n  resources: [\"statefulsets\"]\n  verbs: [\"get\", \"list\", \"patch\"]","handlingStrategy":"validation","validationCode":"allowed, err := k8sClient.CanI(ns, client.StsGVR, name, client.PatchAccess)\nif err != nil { return err }\nif !allowed {\n    return fmt.Errorf(\"missing patch on statefulsets.apps in %s — run: kubectl auth can-i patch statefulsets -n %s\", ns, ns)\n}","typeGuard":null,"tryCatchPattern":"if err := stsDAO.SetImages(ctx, path, specs); err != nil {\n    if strings.Contains(err.Error(), \"not authorized\") {\n        // guide user to RBAC fix instead of retrying blindly\n        return showRbacHint(\"patch\", \"statefulsets.apps\", ns)\n    }\n    return err\n}","preventionTips":["Pre-check with kubectl auth can-i patch statefulsets -n <ns> when switching contexts/users","Hide or grey out image-swap actions when the self-access review denies patch","Keep RBAC bindings for CI/viewer accounts minimal but include patch where image updates are expected"],"tags":["kubernetes","rbac","statefulset","authorization","k9s"],"backgroundTag":null,"analyzedSha":"2d3ccc6ba2ce98c3781bfc441bb3e884f072774f","analyzedAt":"2026-08-15T16:09:14.432Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}