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

  1. Re-run `kops update cluster --yes` to regenerate nodeup config with KeypairIDs populated, then restart nodeup / replace the instance
  2. Ensure nodeup binary and cluster kOps version match (`kops rolling-update cluster` refreshes assets)
  3. Verify `kops get keypairs` shows the keyset and note its primary ID; confirm it appears in the node's nodeup.conf
  4. 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

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


AI-assisted analysis of kubernetes/kops@4c8573c808 (2026-09-05). Data as JSON: /api/errors/8126f9efe7a89cdb. Report an issue: GitHub.