{"record":{"id":"2d6b043b1ab5eb7a","repo":"kubernetes/kops","slug":"keypair-has-no-private-key","errorCode":null,"errorMessage":"keypair has no private key","messagePattern":"keypair has no private key","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/kops/promote_keypair.go","lineNumber":185,"sourceCode":"\t\t\tif item.PrivateKey != nil && item.DistrustTimestamp == nil && item.Certificate != nil {\n\t\t\t\titemId, ok := big.NewInt(0).SetString(id, 10)\n\t\t\t\tif ok && highestCandidateId.Cmp(itemId) < 0 {\n\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","sourceCodeStart":167,"sourceCodeEnd":203,"githubUrl":"https://github.com/kubernetes/kops/blob/4c8573c808a73d578c5eadc86d410646ea0b0d73/cmd/kops/promote_keypair.go#L167-L203","documentation":"The specified keypair item exists but has no private key stored (item.PrivateKey == nil). Only keypairs holding both a private key and a certificate can become the primary signing keypair; certificates-only items (e.g. legacy entries or keysets where private keys are not retained) cannot be promoted.","triggerScenarios":"`kops promote keypair <keyset> <id>` where the item for <id> exists in keyset.Items but its PrivateKey is nil — the keypair was issued/stored as certificate-only, or the private-key portion was removed/distrusted from the store.","commonSituations":"Promoting an old CA keypair whose private key was pruned during cleanup; keyset entries created only with certificates (e.g. imported certs without keys); automation referencing an ID from `kops get keypairs` output that lists cert-only entries.","solutions":["Pick a keypair ID that includes a private key — `kops get keypairs <keyset>` shows which entries are usable for promotion.","Omit the ID entirely (`kops promote keypair <keyset>`) so kOps auto-selects the newest candidate that has a private key and is not distrusted.","If no keypair with a private key exists, issue a new one (`kops create keypair <keyset>`) and then promote it.","Restore the private key from a state-store backup if it was deleted inadvertently."],"exampleFix":"// before: ID refers to a certificate-only item\nkops promote keypair kubernetes-ca 1234567890 --name c.example.com\n// error: keypair has no private key\n// after: let kOps choose the newest keypair with a private key\nkops promote keypair kubernetes-ca --name c.example.com","handlingStrategy":"validation","validationCode":"// shell: confirm the item has a usable private key before promoting\n// (auto-promote mode already filters on PrivateKey != nil; when passing an ID, verify first)\nkops get keypairs \"$KEYSET\" --name \"$CLUSTER\" --state \"$KOPS_STATE_STORE\" -o json \\\n  | jq -e --arg id \"$KEYPAIR_ID\" '.items[] | select(.id == $id) | .privateKey != null' >/dev/null \\\n  || { echo \"$KEYPAIR_ID has no private key; omit the ID to auto-select a promotable keypair\"; exit 1; }","typeGuard":null,"tryCatchPattern":"if kops promote keypair \"$KEYSET\" \"$KEYPAIR_ID\" --name \"$CLUSTER\" 2>&1 | grep -q 'has no private key'; then\n  echo \"$KEYPAIR_ID is certificate-only; promoting newest keypair with a private key instead\"\n  kops promote keypair \"$KEYSET\" --name \"$CLUSTER\"\nelse\n  kops promote keypair \"$KEYSET\" \"$KEYPAIR_ID\" --name \"$CLUSTER\"\nfi","preventionTips":["Prefer omitting the keypair ID so kOps only considers items with a private key.","Verify an entry holds both a certificate and a private key via `kops get keypairs` before promoting a specific ID.","Avoid deleting or pruning private keys from the keystore while old entries may still be needed.","Issue a fresh keypair (`kops create keypair`) when only certificate-only entries remain."],"tags":["keypair","private-key","keystore","validation"],"backgroundTag":"missing-private-key","analyzedSha":"4c8573c808a73d578c5eadc86d410646ea0b0d73","analyzedAt":"2026-09-05T04:13:19.212Z","contentChangedAt":"2026-09-05T04:13:19.212Z","schemaVersion":2},"datasetVersion":"2026-09-12T12:17:11.808Z"}