{"record":{"id":"ac95b6a1237f3977","repo":"kubernetes/kops","slug":"found-cert-in-store-but-did-not-find-private-key","errorCode":null,"errorMessage":"found cert in store, but did not find private key: %q","messagePattern":"found cert in store, but did not find private key: %q","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"upup/pkg/fi/fitasks/keypair.go","lineNumber":89,"sourceCode":"\nfunc (e *Keypair) Find(c *fi.CloudupContext) (*Keypair, error) {\n\tctx := c.Context()\n\n\tname := fi.ValueOf(e.Name)\n\tif name == \"\" {\n\t\treturn nil, nil\n\t}\n\n\tkeyset, err := c.T.Keystore.FindKeyset(ctx, name)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif keyset == nil || keyset.Primary == nil || keyset.Primary.Certificate == nil {\n\t\treturn nil, nil\n\t}\n\tcert := keyset.Primary.Certificate\n\tif keyset.Primary.PrivateKey == nil {\n\t\treturn nil, fmt.Errorf(\"found cert in store, but did not find private key: %q\", name)\n\t}\n\n\tvar alternateNames []string\n\talternateNames = append(alternateNames, cert.Certificate.DNSNames...)\n\talternateNames = append(alternateNames, cert.Certificate.EmailAddresses...)\n\tfor _, ip := range cert.Certificate.IPAddresses {\n\t\talternateNames = append(alternateNames, ip.String())\n\t}\n\tsort.Strings(alternateNames)\n\n\tactual := &Keypair{\n\t\tName:           &name,\n\t\tAlternateNames: alternateNames,\n\t\tSubject:        pki.PkixNameToString(&cert.Subject),\n\t\tIssuer:         pki.PkixNameToString(&cert.Certificate.Issuer),\n\t\tType:           pki.BuildTypeDescription(cert.Certificate),\n\t\tLegacyFormat:   keyset.LegacyFormat,\n\t}","sourceCodeStart":71,"sourceCodeEnd":107,"githubUrl":"https://github.com/kubernetes/kops/blob/4c8573c808a73d578c5eadc86d410646ea0b0d73/upup/pkg/fi/fitasks/keypair.go#L71-L107","documentation":"During Find, the Keypair task fetches the keyset named `name` from the keystore. If a certificate exists as the primary item but its associated private key is missing, kops cannot reconstruct the task's actual state (the keypair is unusable), so it fails hard instead of treating the entry as absent. This indicates a partially-written or corrupted keyset in the keystore (e.g. the keybase/secret store).","triggerScenarios":"c.T.Keystore.FindKeyset returns a keyset whose Primary.Certificate is set but Primary.PrivateKey is nil; occurs during any cluster update where the affected keypair task runs Find (e.g. `kops update cluster`, `kops replace`).","commonSituations":"Keyset was manually edited in the secret store and the .key file deleted; a failed/partial write left only the cert; mirroring or migrating a cluster state store and dropping private key files; keystore backed by git/S3 where key files were excluded by ignore rules.","solutions":["Restore the missing private key file in the state store (e.g. in the keyset directory under the cluster's pki/secret store) from backup","Delete the incomplete keyset so the next update regenerates it: `kops delete secret keypair <name> --name <cluster>` (or remove the keyset directory), then `kops update cluster --yes`","Check the state store (S3/GCS/VFSSL/Vault) for the item named <name> and compare which files (cert vs key) are present","If rotation is acceptable, rotate the affected keypair rather than restoring"],"exampleFix":"// before: state store has only <name>.crt for keyset\n// after\nkops delete secret keypair apiserver --name mycluster.example.com\nkops update cluster --yes   # regenerates cert+key together","handlingStrategy":"validation","validationCode":"ks, err := keystore.FindKeyset(ctx, name); if err != nil { return err }; if ks != nil && ks.Primary != nil && ks.Primary.Certificate != nil && ks.Primary.PrivateKey == nil { return fmt.Errorf(\"keyset %q is incomplete (cert without key); restore or delete it\", name) }","typeGuard":"func keysetComplete(ks *fi.Keyset) bool { return ks != nil && ks.Primary != nil && ks.Primary.Certificate != nil && ks.Primary.PrivateKey != nil }","tryCatchPattern":"if err != nil { if strings.Contains(err.Error(), \"did not find private key\") { // restore from backup or delete keyset to force regeneration } return err }","preventionTips":["Never hand-edit or partially copy files in the state store's pki directory","Back up the state store including all key files","Exclude nothing keypair-related from sync/ignore rules when mirroring state","Use `kops toolbox dump` / secret listing to verify keyset completeness after migrations"],"tags":["pki","keystore","corrupt-state","tls"],"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-12T07:17:12.445Z"}