{"record":{"id":"064c727a9e3cc9bc","repo":"kubernetes/kops","slug":"error-updating-sshcredential-v","errorCode":null,"errorMessage":"error updating SSHCredential: %v","messagePattern":"error updating SSHCredential: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"upup/pkg/fi/clientset_castore.go","lineNumber":281,"sourceCode":"\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\n}\n\n// deleteSSHCredential deletes the SSHCredential from the registry.\nfunc (c *ClientsetCAStore) deleteSSHCredential(ctx context.Context) error {\n\tclient := c.clientset.SSHCredentials(c.namespace)\n\terr := client.Delete(ctx, \"admin\", metav1.DeleteOptions{})\n\tif err != nil {\n\t\treturn fmt.Errorf(\"error deleting SSHCredential: %v\", err)\n\t}\n\treturn nil\n}\n\n// AddSSHPublicKey implements CAStore::AddSSHPublicKey\nfunc (c *ClientsetCAStore) AddSSHPublicKey(ctx context.Context, pubkey []byte) error {\n\t_, _, _, _, err := ssh.ParseAuthorizedKey(pubkey)","sourceCodeStart":263,"sourceCodeEnd":299,"githubUrl":"https://github.com/kubernetes/kops/blob/4c8573c808a73d578c5eadc86d410646ea0b0d73/upup/pkg/fi/clientset_castore.go#L263-L299","documentation":"addSSHCredential updates the existing 'admin' SSHCredential via client.Update when it already exists. Any Update failure is wrapped as 'error updating SSHCredential: %v'. Typical cause is a resourceVersion conflict or RBAC denial.","triggerScenarios":"AddSSHPublicKey called when the 'admin' SSHCredential exists and client.Update fails: stale resourceVersion (concurrent modification), Forbidden by RBAC, or API server/etcd errors.","commonSituations":"Two operators running `kops create sshpublickey` simultaneously; automation rewriting the object between Get and Update; service account lacking update permission.","solutions":["Re-run the command — a fresh Get updates the resourceVersion and usually resolves conflicts","Serialize SSH key changes (avoid concurrent writers to the same cluster)","Check RBAC grants update on sshcredentials.kops.k8s.io","If persistently failing, delete and re-add the credential"],"exampleFix":"// before\nclient.Update(ctx, sshCredential, metav1.UpdateOptions{})\n// after (retry-on-conflict at caller level)\nfor i := 0; i < 3; i++ {\n\tif err := store.AddSSHPublicKey(ctx, pub); err == nil {\n\t\tbreak\n\t} else if !strings.Contains(err.Error(), \"conflict\") {\n\t\treturn err\n\t}\n\ttime.Sleep(time.Duration(i+1) * time.Second)\n}","handlingStrategy":"retry","validationCode":"// re-read to refresh resourceVersion before updating\n_, err := clientset.SSHCredentials(ns).Get(ctx, \"admin\", metav1.GetOptions{})\nif err != nil { return err }","typeGuard":"func isConflict(err error) bool { return apierrors.IsConflict(err) }","tryCatchPattern":"for i := 0; i < 3; i++ {\n\terr := store.AddSSHPublicKey(ctx, pub)\n\tif err == nil || !isConflict(err) { return err }\n\ttime.Sleep(time.Duration(1<<i) * 100 * time.Millisecond)\n}\nreturn nil","preventionTips":["Serialize SSH credential writes; avoid parallel kops runs","Always re-Get before Update so resourceVersion is fresh","Check the wrapped cause for 'the object has been modified' conflicts","Grant update on sshcredentials.kops.k8s.io"],"tags":["kubernetes","ssh","conflict","rbac"],"backgroundTag":"resourceversion-conflict","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"}