{"record":{"id":"9b67ce0c06e69374","repo":"kubernetes/kops","slug":"keypair-has-no-certificate","errorCode":null,"errorMessage":"keypair has no certificate","messagePattern":"keypair has no certificate","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/kops/promote_keypair.go","lineNumber":188,"sourceCode":"\t\t\t\t\thighestCandidateId = itemId\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tkeypairID = highestCandidateId.String()\n\t\tif keypairID == keyset.Primary.Id {\n\t\t\tfmt.Fprintf(out, \"No %s keypair newer than current primary %s\\n\", name, keypairID)\n\t\t\treturn nil\n\t\t}\n\t} else if item := keyset.Items[keypairID]; item != nil {\n\t\tif item.DistrustTimestamp != nil {\n\t\t\treturn fmt.Errorf(\"keypair is distrusted\")\n\t\t}\n\t\tif item.PrivateKey == nil {\n\t\t\treturn fmt.Errorf(\"keypair has no private key\")\n\t\t}\n\t\tif item.Certificate == nil {\n\t\t\treturn fmt.Errorf(\"keypair has no certificate\")\n\t\t}\n\t} else {\n\t\treturn fmt.Errorf(\"keypair not found\")\n\t}\n\n\tkeyset.Primary = keyset.Items[keypairID]\n\terr = keyStore.StoreKeyset(ctx, name, keyset)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"writing keyset: %v\", err)\n\t}\n\n\tfmt.Fprintf(out, \"Promoted %s %s\\n\", name, keypairID)\n\treturn nil\n}\n\nfunc completePromoteKeyset(ctx context.Context, f commandutils.Factory, options *PromoteKeypairOptions, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {\n\tcommandutils.ConfigureKlogForCompletion()\n","sourceCodeStart":170,"sourceCodeEnd":206,"githubUrl":"https://github.com/kubernetes/kops/blob/4c8573c808a73d578c5eadc86d410646ea0b0d73/cmd/kops/promote_keypair.go#L170-L206","documentation":"This error is returned by promoteKeypair when the `kops promote keypair` command is asked to promote a specific keypair (by ID) whose item exists in the keyset but has no associated certificate. A keypair without a certificate cannot serve as the primary/active key because clients and the control plane verify identities via the certificate, so kOps refuses to promote it. It is a safety check to prevent making a half-created (certificate-only not yet issued) keypair the cluster's active key.","triggerScenarios":"Running `kops promote keypair <name> <keypairID>` (or calling RunPromoteKeypair with a non-empty KeypairID) where keyset.Items[keypairID] exists but item.Certificate == nil — typically a keypair created/rotated whose certificate issuance has not completed, or a keypair item that only holds a private key.","commonSituations":"Operators mid-rotation who grabbed a keypair ID from `kops get keypairs` too early, before the new keypair's certificate was issued; keyset entries left incomplete after a failed or interrupted rotation; manual edits or partial state in the keyset store (e.g. S3/base-store) that dropped the certificate.","solutions":["Wait for the keypair's certificate to be issued (re-run rotation/creation) and verify with `kops get keypairs <name> --type <keyset>` that the ID has a certificate, then re-run the promote.","If keypairID was specified manually, omit it and let promoteKeypair auto-select the highest candidate (it only picks items with PrivateKey AND Certificate, excluding distrusted ones).","If the keyset item is permanently incomplete (e.g. corrupted store state), delete the broken keypair entry (`kops delete keypair <name> <keypairID> --yes`) or re-import/reissue the keyset, then promote a valid ID.","Inspect the keyset backing store directly (state store path for the keyset) to confirm the certificate file is present and not empty; restore it if it was lost."],"exampleFix":"// before: promoting an incomplete keypair by explicit ID\n// kops promote keypair ca 3   ->  \"keypair has no certificate\"\n// after: let kOps pick a fully issued candidate\n// kops promote keypair ca     (auto-selects newest keypair with cert + private key)\n// or verify first:\n// kops get keypairs ca --type ca   # confirm the target ID shows a certificate before promoting","handlingStrategy":"validation","validationCode":"item, ok := keyset.Items[keypairID]\nif ok && item.DistrustTimestamp == nil && item.PrivateKey != nil && item.Certificate != nil {\n    // safe to promote\n}\n// or CLI-side: kops get keypairs <name> and confirm the target ID has a certificate before promoting","typeGuard":"func promotable(item *fi.KeysetItem) bool {\n\treturn item != nil && item.DistrustTimestamp == nil && item.PrivateKey != nil && item.Certificate != nil\n}","tryCatchPattern":null,"preventionTips":["Prefer omitting keypairID so promoteKeypair auto-selects only fully issued candidates.","Always verify with `kops get keypairs <name>` that the target ID shows a certificate before specifying it.","Complete rotation/issuance before promoting; don't promote a keypair mid-creation.","Automate promotes only on items that pass the promotable() checks."],"tags":["keypair","keystore","rotation","cluster-administration"],"backgroundTag":"keypair-missing-certificate","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"}