{"record":{"id":"122f625ae422558f","repo":"kubernetes/kops","slug":"did-not-find-keypair-s-for-s","errorCode":null,"errorMessage":"did not find keypair %s for %s","messagePattern":"did not find keypair (.+?) for (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"nodeup/pkg/model/context.go","lineNumber":402,"sourceCode":"\t// keypair so that the node will be updated when the primary keypair does\n\t// not match the one that we are using.\n\tkeypairID := c.NodeupConfig.KeypairIDs[name]\n\tif keypairID == \"\" {\n\t\t// kOps bug where KeypairID was not populated for the node role.\n\t\treturn fmt.Errorf(\"no keypair ID for %q\", name)\n\t}\n\n\tkeyset, err := c.KeyStore.FindKeyset(ctx.Context(), name)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif keyset == nil {\n\t\treturn fmt.Errorf(\"keyset %q not found\", name)\n\t}\n\n\titem := keyset.Items[keypairID]\n\tif item == nil {\n\t\treturn fmt.Errorf(\"did not find keypair %s for %s\", keypairID, name)\n\t}\n\n\tif includeCert {\n\t\tcertificate := item.Certificate\n\t\tif certificate == nil {\n\t\t\treturn fmt.Errorf(\"certificate %q not found\", name)\n\t\t}\n\n\t\tcert, err := certificate.AsString()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tctx.AddTask(&nodetasks.File{\n\t\t\tPath:           p + \".crt\",\n\t\t\tContents:       fi.NewStringResource(cert),\n\t\t\tType:           nodetasks.FileType_File,\n\t\t\tMode:           s(\"0600\"),","sourceCodeStart":384,"sourceCodeEnd":420,"githubUrl":"https://github.com/kubernetes/kops/blob/4c8573c808a73d578c5eadc86d410646ea0b0d73/nodeup/pkg/model/context.go#L384-L420","documentation":"The keyset exists, but the specific item (certificate+key pair) addressed by the keypairID from NodeupConfig.KeypairIDs is not in keyset.Items. The node is pinned to a keypair ID that was rotated away or deleted from the state store.","triggerScenarios":"keyset.Items[keypairID] is nil because the keypairID recorded in nodeup config was retired by `kops delete keypair`/rotation pruning, or the nodeup config was generated before a rotation that removed old keypairs.","commonSituations":"After certificate rotation with retention cleanup, nodes that haven't updated their config still reference pruned keypair IDs; restoring a state store that lacks historical keypairs.","solutions":["Re-run `kops update cluster --yes` so node configs reference the current primary keypair ID, then rolling-update the nodes","Check available IDs with `kops get keypairs <name> -o yaml` and ensure old keypairs weren't pruned before nodes were updated","Increase keyset retention (or re-issue) with `kops create keypair <name>` so the referenced ID exists again","Restart nodeup after config refresh so it picks up the new KeypairIDs"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"keyset, _ := keyStore.FindKeyset(ctx, name)\nif keyset != nil && keyset.Items[nodeupConfig.KeypairIDs[name]] == nil {\n    return fmt.Errorf(\"keypair %s for %s pruned; refresh node config or re-issue keypair\", nodeupConfig.KeypairIDs[name], name)\n}","typeGuard":null,"tryCatchPattern":"if err := c.BuildCertificatePairTask(ctx, name, path, filename, owner, nil); err != nil {\n    var pinErr = \"did not find keypair\"\n    if strings.Contains(err.Error(), pinErr) {\n        klog.Warningf(\"pinned keypair retired for %s; refresh nodeup config / re-issue keypair\", name)\n    }\n    return err\n}","preventionTips":["Rolling-update nodes before pruning old keypairs (`kops delete keypair` only after full rotation)","Check `kops get keypairs <name> -o yaml` for available IDs before cleanup","Keep keyset retention generous in large slow-rolling clusters","Regenerate nodeup configs after every rotation"],"tags":["go","nodeup","keypair","rotation"],"backgroundTag":"keypair-id-mismatch","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"}