kubernetes/kops · error

error reading SSH key file %q: %v

Error message

error reading SSH key file %q: %v

What it means

When --ssh-public-key is passed to `kops update cluster` (deprecated), the command reads the referenced file to install it into the cluster's SSH credential store. If os.ReadFile fails — missing file, bad path, permission denied — the error is wrapped as `error reading SSH key file %q: %v`. The path is first expanded via utils.ExpandPath, so ~ resolves to the home directory.

Source

Thrown at cmd/kops/update_cluster.go:298

	}

	sshCredentialStore, err := clientset.SSHCredentialStore(cluster)
	if err != nil {
		return results, err
	}

	secretStore, err := clientset.SecretStore(cluster)
	if err != nil {
		return results, err
	}

	if c.SSHPublicKey != "" {
		fmt.Fprintf(out, "--ssh-public-key on update is deprecated - please use `kops create secret --name %s sshpublickey admin -i ~/.ssh/id_rsa.pub` instead\n", cluster.ObjectMeta.Name)

		c.SSHPublicKey = utils.ExpandPath(c.SSHPublicKey)
		authorized, err := os.ReadFile(c.SSHPublicKey)
		if err != nil {
			return results, fmt.Errorf("error reading SSH key file %q: %v", c.SSHPublicKey, err)
		}
		err = sshCredentialStore.AddSSHPublicKey(ctx, authorized)
		if err != nil {
			return results, fmt.Errorf("error adding SSH public key: %v", err)
		}

		klog.Infof("Using SSH public key: %v\n", c.SSHPublicKey)
	}

	var phase cloudup.Phase
	if c.Phase != "" {
		switch strings.ToLower(c.Phase) {
		case string(cloudup.PhaseNetwork):
			phase = cloudup.PhaseNetwork
		case string(cloudup.PhaseSecurity), "iam": // keeping IAM for backwards compatibility
			phase = cloudup.PhaseSecurity
		case string(cloudup.PhaseCluster):
			phase = cloudup.PhaseCluster

View on GitHub (pinned to 4c8573c808)

Solutions

  1. Check the file exists and is readable: `ls -l <path>` (after ~ expansion)
  2. Prefer the supported command: `kops create secret --name <cluster> sshpublickey admin -i <path>`
  3. Remove --ssh-public-key from the update invocation once the key is stored

Example fix

// before
kops update cluster c.k8s.local --ssh-public-key ~/.ssh/id_ed25519.pub --yes
// after (file exists, or better:)
kops create secret --name c.k8s.local sshpublickey admin -i ~/.ssh/id_ed25519.pub
kops update cluster c.k8s.local --yes
Defensive patterns

Strategy: validation

Validate before calling

path := utils.ExpandPath(c.SSHPublicKey)
info, err := os.Stat(path)
if err != nil || info.IsDir() {
	return fmt.Errorf("--ssh-public-key %q is not a readable file", path)
}
// then run kops update cluster --ssh-public-key <path>

Prevention

When it happens

Trigger: `kops update cluster --ssh-public-key <path>` where <path> does not exist, is a directory, or is unreadable by the current user (after ~ expansion).

Common situations: Deprecated flag usage in old scripts; file moved/renamed (id_rsa.pub regenerated); running in CI container where the key was never mounted; wrong relative path after changing working directory; permissions tightened by ssh-keygen.

Understand the failure class

Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.

Related errors


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