kubernetes/kops · error

no keypair ID for %q

Error message

no keypair ID for %q

What it means

The node discovery service builder builds a JWKS from the 'service-account' keypair. It first looks up the keypair ID in NodeupConfig.KeypairIDs; older kOps versions had a bug leaving KeypairID unpopulated for the node role, so when the map entry is empty this error is returned and the discovery-service task fails.

Source

Thrown at nodeup/pkg/model/discovery_service.go:108

		ClientKey:         keyResource,
		ClientCA:          caResource,
		RegisterName:      id.Name,
		RegisterNamespace: id.Namespace,
		JWKS:              jwks,
	}
	c.AddTask(registerTask)

	return nil
}

func findJWKSForServiceAccount(ctx context.Context, keypairIDs map[string]string, keystore fi.KeystoreReader) ([]nodetasks.JSONWebKey, error) {
	var jwks []nodetasks.JSONWebKey

	name := "service-account"
	keypairID := keypairIDs[name]
	if keypairID == "" {
		// kOps bug where KeypairID was not populated for the node role.
		return nil, fmt.Errorf("no keypair ID for %q", name)
	}

	keyset, err := keystore.FindKeyset(ctx, name)
	if err != nil {
		return nil, err
	}
	if keyset == nil {
		return nil, fmt.Errorf("keyset %q not found", name)
	}

	for _, item := range keyset.Items {
		if item.DistrustTimestamp != nil {
			continue
		}
		if item.Certificate == nil || item.Certificate.Subject.CommonName != "service-account" {
			continue
		}

View on GitHub (pinned to 4c8573c808)

Solutions

  1. Re-run `kops update cluster --refresh-cluster-issue-certificates` / `kops update cluster` to regenerate nodeup config with populated KeypairIDs
  2. Ensure a service-account key exists: `kops get keypairs service-account --type secrets` ; if missing, re-issue via `kops update cluster`
  3. Run `kops upgrade cluster` and re-apply so the node role's KeypairID is populated
  4. Regenerate the node's bootstrap config with a current kops binary

Example fix

# regenerate config so KeypairIDs are populated
kops update cluster mycluster.example.com --yes --admin
kops rolling-update cluster mycluster.example.com --yes
Defensive patterns

Strategy: validation

Validate before calling

// before building the discovery service, ensure the keypair ID is populated
kpID := nodeupConfig.KeypairIDs["service-account"]
if kpID == "" {
	// regenerate nodeup config: kops update cluster <cluster> --yes
	return fmt.Errorf("service-account keypair ID missing in nodeup config; re-run kops update cluster")
}

Try / catch

jwks, err := findJWKSForServiceAccount(ctx, keypairIDs, keystore)
if err != nil {
	if strings.Contains(err.Error(), "no keypair ID") {
		// known kOps bug for old node roles: regenerate bootstrap config and retry
		return fmt.Errorf("stale nodeup config (missing KeypairID); re-run kops update cluster: %w", err)
	}
	return err
}

Prevention

When it happens

Trigger: findJWKSForServiceAccount is called (from discovery_service Build) with keypairIDs["service-account"] empty — i.e. the node's NodeupConfig was generated by a kOps version that did not populate KeypairID for the node role, or the service-account keypair was never created/registered.

Common situations: Upgrading an old cluster where node bootstrap config predates KeypairIDs population; cluster state missing the service-account key; running nodeup from a newer binary against stale nodeup config generated by a buggy older kOps.

Related errors


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