{"record":{"id":"bf26f4507d932006","repo":"kubernetes/kops","slug":"error-reading-sshcredential-v","errorCode":null,"errorMessage":"error reading SSHCredential: %v","messagePattern":"error reading SSHCredential: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"upup/pkg/fi/clientset_castore.go","lineNumber":266,"sourceCode":"\t\t}\n\t} else {\n\t\tif _, err := client.Update(ctx, kopsKeyset, metav1.UpdateOptions{}); err != nil {\n\t\t\treturn fmt.Errorf(\"error updating keyset %q: %v\", name, err)\n\t\t}\n\t}\n\treturn nil\n}\n\n// addSSHCredential saves the specified SSH Credential to the registry, doing an update or insert\nfunc (c *ClientsetCAStore) addSSHCredential(ctx context.Context, publicKey string) error {\n\tcreate := false\n\tclient := c.clientset.SSHCredentials(c.namespace)\n\tsshCredential, err := client.Get(ctx, \"admin\", metav1.GetOptions{})\n\tif err != nil {\n\t\tif errors.IsNotFound(err) {\n\t\t\tsshCredential = nil\n\t\t} else {\n\t\t\treturn fmt.Errorf(\"error reading SSHCredential: %v\", err)\n\t\t}\n\t}\n\tif sshCredential == nil {\n\t\tsshCredential = &kops.SSHCredential{}\n\t\tsshCredential.Name = \"admin\"\n\t\tcreate = true\n\t}\n\tsshCredential.Spec.PublicKey = publicKey\n\tif create {\n\t\tif _, err := client.Create(ctx, sshCredential, metav1.CreateOptions{}); err != nil {\n\t\t\treturn fmt.Errorf(\"error creating SSHCredential: %v\", err)\n\t\t}\n\t} else {\n\t\tif _, err := client.Update(ctx, sshCredential, metav1.UpdateOptions{}); err != nil {\n\t\t\treturn fmt.Errorf(\"error updating SSHCredential: %v\", err)\n\t\t}\n\t}\n\treturn nil","sourceCodeStart":248,"sourceCodeEnd":284,"githubUrl":"https://github.com/kubernetes/kops/blob/4c8573c808a73d578c5eadc86d410646ea0b0d73/upup/pkg/fi/clientset_castore.go#L248-L284","documentation":"addSSHCredential first GETs the 'admin' SSHCredential from the cluster via the clientset. If Get fails with an error other than IsNotFound, it returns 'error reading SSHCredential: %v'. Only a clean 404 is treated as 'does not exist yet'.","triggerScenarios":"AddSSHPublicKey invoked while `client.Get(ctx, \"admin\", ...)` fails: API server unreachable, RBAC denies get on sshcredentials.kops.k8s.io, malformed request, or timeout.","commonSituations":"Running `kops create sshpublickey` with a kubeconfig lacking RBAC rights; API server down or behind a broken load balancer; wrong --state/namespace so the resource group is invalid.","solutions":["Read the wrapped %v error to distinguish authz (Forbidden) from connectivity","Confirm RBAC grants get on sshcredentials.kops.k8s.io in the kops namespace","Verify cluster/API server connectivity with kubectl","Retry the AddSSHPublicKey command after transient network issues"],"exampleFix":"// before\nerr := store.AddSSHPublicKey(ctx, pub)\n// after\nif err := store.AddSSHPublicKey(ctx, pub); err != nil {\n\tif strings.Contains(err.Error(), \"forbidden\") {\n\t\treturn fmt.Errorf(\"grant RBAC get on sshcredentials.kops.k8s.io: %w\", err)\n\t}\n\treturn err\n}","handlingStrategy":"validation","validationCode":"// verify read access before AddSSHPublicKey\nif _, err := clientset.SSHCredentials(ns).Get(ctx, \"admin\", metav1.GetOptions{}); err != nil && !apierrors.IsNotFound(err) {\n\treturn fmt.Errorf(\"precondition failed: %w\", err)\n}","typeGuard":"func isNotFound(err error) bool { return apierrors.IsNotFound(err) }","tryCatchPattern":"if err := store.AddSSHPublicKey(ctx, pub); err != nil {\n\tif !isNotFound(err) { log.Printf(\"read sshcredential failed: %v\", err) }\n\treturn err\n}","preventionTips":["Verify kubectl can reach the cluster API before kops operations","Grant get on sshcredentials.kops.k8s.io to the operating identity","Use the same kubeconfig for kubectl diagnostics and kops","Treat only clean NotFound as 'missing'; all else is a real failure"],"tags":["kubernetes","ssh","rbac","api-server"],"backgroundTag":"kubernetes-api-read-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"}