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.PhaseClusterView on GitHub (pinned to 4c8573c808)
Solutions
- Check the file exists and is readable: `ls -l <path>` (after ~ expansion)
- Prefer the supported command: `kops create secret --name <cluster> sshpublickey admin -i <path>`
- 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
- Stat the key file before invoking update
- Migrate to `kops create secret sshpublickey admin -i <file>` — the update flag is deprecated
- Mount the key explicitly in CI containers and use absolute paths
- Note that ~ is expanded by kOps, not the shell, when the flag is quoted
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
- error reading SSH key file %q: %v
- error reading SSH public key files %q: %v
- error reading file %q: %v
- must specify %q label with cluster name to create SSHCredent
- spec.PublicKey is required
AI-assisted analysis of kubernetes/kops@4c8573c808 (2026-09-05).
Data as JSON: /api/errors/72556346a72f96b8.
Report an issue: GitHub.