kubernetes/kops · error

keypair has no private key

Error message

keypair has no private key

What it means

The specified keypair item exists but has no private key stored (item.PrivateKey == nil). Only keypairs holding both a private key and a certificate can become the primary signing keypair; certificates-only items (e.g. legacy entries or keysets where private keys are not retained) cannot be promoted.

Source

Thrown at cmd/kops/promote_keypair.go:185

			if item.PrivateKey != nil && item.DistrustTimestamp == nil && item.Certificate != nil {
				itemId, ok := big.NewInt(0).SetString(id, 10)
				if ok && highestCandidateId.Cmp(itemId) < 0 {
					highestCandidateId = itemId
				}
			}
		}

		keypairID = highestCandidateId.String()
		if keypairID == keyset.Primary.Id {
			fmt.Fprintf(out, "No %s keypair newer than current primary %s\n", name, keypairID)
			return nil
		}
	} else if item := keyset.Items[keypairID]; item != nil {
		if item.DistrustTimestamp != nil {
			return fmt.Errorf("keypair is distrusted")
		}
		if item.PrivateKey == nil {
			return fmt.Errorf("keypair has no private key")
		}
		if item.Certificate == nil {
			return fmt.Errorf("keypair has no certificate")
		}
	} else {
		return fmt.Errorf("keypair not found")
	}

	keyset.Primary = keyset.Items[keypairID]
	err = keyStore.StoreKeyset(ctx, name, keyset)
	if err != nil {
		return fmt.Errorf("writing keyset: %v", err)
	}

	fmt.Fprintf(out, "Promoted %s %s\n", name, keypairID)
	return nil
}

View on GitHub (pinned to 4c8573c808)

Solutions

  1. Pick a keypair ID that includes a private key — `kops get keypairs <keyset>` shows which entries are usable for promotion.
  2. Omit the ID entirely (`kops promote keypair <keyset>`) so kOps auto-selects the newest candidate that has a private key and is not distrusted.
  3. If no keypair with a private key exists, issue a new one (`kops create keypair <keyset>`) and then promote it.
  4. Restore the private key from a state-store backup if it was deleted inadvertently.

Example fix

// before: ID refers to a certificate-only item
kops promote keypair kubernetes-ca 1234567890 --name c.example.com
// error: keypair has no private key
// after: let kOps choose the newest keypair with a private key
kops promote keypair kubernetes-ca --name c.example.com
Defensive patterns

Strategy: validation

Validate before calling

// shell: confirm the item has a usable private key before promoting
// (auto-promote mode already filters on PrivateKey != nil; when passing an ID, verify first)
kops get keypairs "$KEYSET" --name "$CLUSTER" --state "$KOPS_STATE_STORE" -o json \
  | jq -e --arg id "$KEYPAIR_ID" '.items[] | select(.id == $id) | .privateKey != null' >/dev/null \
  || { echo "$KEYPAIR_ID has no private key; omit the ID to auto-select a promotable keypair"; exit 1; }

Try / catch

if kops promote keypair "$KEYSET" "$KEYPAIR_ID" --name "$CLUSTER" 2>&1 | grep -q 'has no private key'; then
  echo "$KEYPAIR_ID is certificate-only; promoting newest keypair with a private key instead"
  kops promote keypair "$KEYSET" --name "$CLUSTER"
else
  kops promote keypair "$KEYSET" "$KEYPAIR_ID" --name "$CLUSTER"
fi

Prevention

When it happens

Trigger: `kops promote keypair <keyset> <id>` where the item for <id> exists in keyset.Items but its PrivateKey is nil — the keypair was issued/stored as certificate-only, or the private-key portion was removed/distrusted from the store.

Common situations: Promoting an old CA keypair whose private key was pruned during cleanup; keyset entries created only with certificates (e.g. imported certs without keys); automation referencing an ID from `kops get keypairs` output that lists cert-only entries.

Related errors


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