kubernetes/kops · error

key %q did not have primary id set

Error message

key %q did not have primary id set

What it means

When includeKeypairID is true, loadCertificates also records the primary keypair's ID in config.KeypairIDs so nodeup can pin/verify the keyset version. This error is thrown when the keyset exists and its certificates serialize fine, but it has no primary item, or the primary item has an empty ID — i.e. no designated 'current' keypair.

Source

Thrown at pkg/nodemodel/nodeupconfigbuilder.go:475

		configServer.Servers = append(configServer.Servers, baseURL.String())
	}

	return configServer
}

func loadCertificates(keysets map[string]*fi.Keyset, name string, config *nodeup.Config, includeKeypairID bool) error {
	keyset := keysets[name]
	if keyset == nil {
		return fmt.Errorf("key %q not found", name)
	}
	certificates, err := keyset.ToCertificateBytes()
	if err != nil {
		return fmt.Errorf("failed to read %q certificates: %w", name, err)
	}
	config.CAs[name] = string(certificates)
	if includeKeypairID {
		if keyset.Primary == nil || keyset.Primary.Id == "" {
			return fmt.Errorf("key %q did not have primary id set", name)
		}
		config.KeypairIDs[name] = keyset.Primary.Id
	}
	return nil
}

// buildWarmPoolImages returns a list of container images that should be pre-pulled during instance pre-initialization
func (n *nodeUpConfigBuilder) buildWarmPoolImages(ig *kops.InstanceGroup) []string {
	if ig == nil || ig.Spec.Role.HasControlPlane() {
		return nil
	}

	images := map[string]bool{}

	// Add component and addon images that impact startup time
	// TODO: Exclude images that only run on control-plane nodes in a generic way
	desiredImagePrefixes := []string{
		// Ignore images hosted in private ECR repositories as containerd cannot actually pull these

View on GitHub (pinned to 4c8573c808)

Solutions

  1. Re-elect/recreate a primary: run `kops update cluster --yes` so kOps elects the newest keypair as primary, then retry.
  2. Use `kops get keypairs <name>` to inspect items; if none is marked primary, promote one (`kops promote keypair` / keyset rotation commands).
  3. If rotation left the keyset inconsistent, complete the rotation (issue/rotate/deregister cycle per kOps keypair rotation docs).
  4. Restore the primary pointer from the state-store backup if it was accidentally deleted.

Example fix

// before: keyset has items but no primary
kops get keypairs kubernetes-ca  # no item marked primary
// after
kops update cluster mycluster.k8s.local --yes  # elects newest keypair as primary
Defensive patterns

Strategy: try-catch

Validate before calling

ks := keysets[name]
if ks != nil && includeKeypairID && (ks.Primary == nil || ks.Primary.Id == "") {
    return fmt.Errorf("keyset %q has no primary keypair; run kops update cluster or promote a keypair", name)
}

Type guard

func hasPrimary(ks *fi.Keyset) bool {
    return ks != nil && ks.Primary != nil && ks.Primary.Id != ""
}

Try / catch

if err := loadCertificates(keysets, name, config, true); err != nil {
    if strings.Contains(err.Error(), "did not have primary id set") {
        // no primary elected: re-run update or promote keypair before retry
        return fmt.Errorf("keyset %q missing primary (%w); run 'kops update cluster --yes' or 'kops promote keypair'", name, err)
    }
    return err
}

Prevention

When it happens

Trigger: BuildConfig -> loadCertificates with includeKeypairID=true on a keyset whose Primary field is nil or whose Primary.Id is an empty string: a keyset whose primary was never elected, a keyset whose primary was rotated/removed leaving no primary, or a keyset constructed programmatically/tests where Primary was not set.

Common situations: Keypair rotation interrupted mid-way leaving no elected primary; manually deleting the primary keypair item so only older items remain; clusters migrated between kOps versions where the primary-ID marker was not yet written; state store sync races where the primary pointer object was deleted.

Related errors


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