kubernetes/kops · error
no keypair ID for %q
Error message
no keypair ID for %q
What it means
buildCertificatePairTask reads the keypair ID for the named keyset from NodeupConfig.KeypairIDs before writing cert/key files. If the ID is empty the nodeup config was not populated for this node role — the code comments call it a kOps bug where KeypairID was not populated for the node role — so it refuses to materialize files from the wrong (primary) keypair.
Source
Thrown at nodeup/pkg/model/context.go:389
// BuildPrivateKeyTask builds a task to create the private key file.
func (c *NodeupModelContext) BuildPrivateKeyTask(ctx *fi.NodeupModelBuilderContext, name, path, filename string, owner *string, beforeServices []string) error {
return c.buildCertificatePairTask(ctx, name, path, filename, owner, beforeServices, false)
}
func (c *NodeupModelContext) buildCertificatePairTask(ctx *fi.NodeupModelBuilderContext, name, path, filename string, owner *string, beforeServices []string, includeCert bool) error {
p := filepath.Join(path, filename)
if !filepath.IsAbs(p) {
p = filepath.Join(c.PathSrvKubernetes(), p)
}
// We use the keypair ID passed in nodeup.Config instead of the primary
// keypair so that the node will be updated when the primary keypair does
// not match the one that we are using.
keypairID := c.NodeupConfig.KeypairIDs[name]
if keypairID == "" {
// kOps bug where KeypairID was not populated for the node role.
return fmt.Errorf("no keypair ID for %q", name)
}
keyset, err := c.KeyStore.FindKeyset(ctx.Context(), name)
if err != nil {
return err
}
if keyset == nil {
return fmt.Errorf("keyset %q not found", name)
}
item := keyset.Items[keypairID]
if item == nil {
return fmt.Errorf("did not find keypair %s for %s", keypairID, name)
}
if includeCert {
certificate := item.Certificate
if certificate == nil {View on GitHub (pinned to 4c8573c808)
Solutions
- Re-run `kops update cluster --yes` to regenerate nodeup config with KeypairIDs populated, then restart nodeup / replace the instance
- Ensure nodeup binary and cluster kOps version match (`kops rolling-update cluster` refreshes assets)
- Verify `kops get keypairs` shows the keyset and note its primary ID; confirm it appears in the node's nodeup.conf
- If it persists, file/check kOps issues — the code itself marks this as a known kOps bug class
Defensive patterns
Strategy: validation
Validate before calling
if kpID := nodeupConfig.KeypairIDs[name]; kpID == "" {
return fmt.Errorf("KeypairIDs[%q] empty in nodeup config; regenerate nodeup config", name)
} Try / catch
err := c.BuildCertificatePairTask(ctx, "kubelet", "/srv/kubernetes", "kubelet", owner, nil)
if err != nil && strings.Contains(err.Error(), "no keypair ID") {
return fmt.Errorf("stale nodeup config for role; re-run kops update cluster: %w", err)
} Prevention
- Never hand-edit nodeup.conf; always generate via kops
- Match nodeup binary version to cluster version
- After kOps upgrades, rolling-update nodes to refresh configs
- Alert on nodeup errors mentioning KeypairID
When it happens
Trigger: Calling BuildCertificatePairTask, BuildPrivateKeyTask, or Build for a keyset name (e.g. "kubelet", "apiserver-proxy") that has no entry in NodeupConfig.KeypairIDs because the serialized nodeup config predates KeypairIDs or was generated without that keypair.
Common situations: Rolling nodes after a kOps upgrade where nodeup.conf on the instance still has the old schema; mixed-version clusters where an old nodeup binary pairs with a new config or vice versa.
Related errors
- building nodeConfig for instanceGroup: %w
- no keypairID for %q
- did not find keypair %s for %s
- no keypair ID for %q
- error building kube-controller-manager flags: %v
AI-assisted analysis of kubernetes/kops@4c8573c808 (2026-09-05).
Data as JSON: /api/errors/8126f9efe7a89cdb.
Report an issue: GitHub.