{"record":{"id":"609585bdd67cff37","repo":"kubernetes/kops","slug":"error-reading-keyset-q-v-609585","errorCode":null,"errorMessage":"error reading Keyset %q: %v","messagePattern":"error reading Keyset %q: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"upup/pkg/fi/secrets/clientset_secretstore.go","lineNumber":158,"sourceCode":"\t}\n\tif s == nil {\n\t\treturn nil, fmt.Errorf(\"Secret not found: %q\", name)\n\t}\n\treturn s, nil\n}\n\n// DeleteSecret implements fi.SecretStore::DeleteSecret\nfunc (c *ClientsetSecretStore) DeleteSecret(name string) error {\n\tctx := context.TODO()\n\n\tclient := c.clientset.Keysets(c.namespace)\n\n\tkeyset, err := client.Get(ctx, name, metav1.GetOptions{})\n\tif err != nil {\n\t\tif errors.IsNotFound(err) {\n\t\t\treturn nil\n\t\t}\n\t\treturn fmt.Errorf(\"error reading Keyset %q: %v\", name, err)\n\t}\n\n\tif keyset.Spec.Type != kops.SecretTypeSecret {\n\t\treturn fmt.Errorf(\"mismatch on Keyset type on %q\", name)\n\t}\n\n\tif err := client.Delete(ctx, name, metav1.DeleteOptions{}); err != nil {\n\t\treturn fmt.Errorf(\"error deleting Keyset %q: %v\", name, err)\n\t}\n\n\treturn nil\n}\n\n// GetOrCreateSecret implements fi.SecretStore::GetOrCreateSecret\nfunc (c *ClientsetSecretStore) GetOrCreateSecret(ctx context.Context, name string, secret *fi.Secret) (*fi.Secret, bool, error) {\n\tfor i := 0; i < 2; i++ {\n\t\ts, err := c.FindSecret(name)\n\t\tif err != nil {","sourceCodeStart":140,"sourceCodeEnd":176,"githubUrl":"https://github.com/kubernetes/kops/blob/4c8573c808a73d578c5eadc86d410646ea0b0d73/upup/pkg/fi/secrets/clientset_secretstore.go#L140-L176","documentation":"This error is returned by ClientsetSecretStore.DeleteSecret when the Get call for the Keyset fails with an error other than NotFound (NotFound is deliberately treated as success). It wraps the underlying client-go error, so the cause (forbidden, timeout, connection refused) is in the %v text. It means the delete could not even verify the keyset's existence or read its type before deleting.","triggerScenarios":"DeleteSecret(name) calls client.Get(ctx, name, metav1.GetOptions{}); the API server returns a non-NotFound error — RBAC denial on keysets/get, API server unreachable, request timeout, or invalid name rejected by validation.","commonSituations":"Running `kops delete secret` with a ServiceAccount lacking get permission on keysets; API server temporarily down; wrong namespace configured; network/VPN interruption during automation.","solutions":["Read the wrapped error to identify the root cause (forbidden vs timeout vs connection)","Verify RBAC allows get (and delete) on keysets in the target namespace","Check API server reachability: kubectl get keysets -n <namespace> <name>","Retry after connectivity is restored — deletion is safe to re-run because NotFound is treated as success","Confirm the correct cluster/namespace before retrying"],"exampleFix":"// before: single attempt fails on transient errors\nif err := store.DeleteSecret(\"admin\"); err != nil { return err }\n// after: retry transient failures; NotFound is already a no-op\nvar lastErr error\nfor i := 0; i < 3; i++ {\n    err := store.DeleteSecret(\"admin\")\n    if err == nil { return nil }\n    lastErr = err\n    if strings.Contains(err.Error(), \"forbidden\") { return err }\n    time.Sleep(time.Duration(i+1) * time.Second)\n}\nreturn lastErr","handlingStrategy":"try-catch","validationCode":"// preflight: confirm get access on the keyset before deleting\nclient := clientset.Keysets(namespace)\nif _, err := client.Get(context.TODO(), NamePrefix+name, metav1.GetOptions{}); err != nil {\n    if errors.IsNotFound(err) { return nil } // already gone\n    return fmt.Errorf(\"preflight get failed: %v\", err)\n}","typeGuard":"func isTransientAPIError(err error) bool {\n    return err != nil && !errors.IsNotFound(err) && !errors.IsForbidden(err)\n}","tryCatchPattern":"err := store.DeleteSecret(name)\nif err != nil && strings.Contains(err.Error(), \"error reading Keyset\") {\n    for i := 0; i < 3; i++ {\n        time.Sleep(time.Duration(1<<i) * time.Second)\n        if err = store.DeleteSecret(name); err == nil { break }\n    }\n}\nif err != nil { return err }","preventionTips":["Ensure the identity has get+delete RBAC on keysets before running deletes","Treat DeleteSecret as idempotent — NotFound already returns nil, so safe retries are fine","Check API server connectivity before bulk delete operations","Confirm the target namespace/cluster to avoid cross-cluster API errors"],"tags":["kubernetes","rbac","kops","secrets","delete"],"backgroundTag":"api-request-failed","analyzedSha":"4c8573c808a73d578c5eadc86d410646ea0b0d73","analyzedAt":"2026-09-05T04:13:19.212Z","contentChangedAt":"2026-09-05T04:13:19.212Z","schemaVersion":2},"datasetVersion":"2026-09-12T07:17:12.445Z"}