kubernetes/kops · error
failed to list ssh keys: %w
Error message
failed to list ssh keys: %w
What it means
Returned by the Hetzner listSSHKeys resource-discovery function when c.GetSSHKeys(clusterName) fails during 'kops delete cluster' for a Hetzner cluster. It wraps the hcloud SDK error so users know SSH key enumeration failed. Discovery cannot continue without listing, so the whole delete operation aborts.
Source
Thrown at pkg/resources/hetzner/resources.go:76
rt, err := fn(cloud, clusterName)
if err != nil {
return nil, err
}
for _, t := range rt {
resourceTrackers[t.Type+":"+t.ID] = t
}
}
return resourceTrackers, nil
}
func listSSHKeys(cloud fi.Cloud, clusterName string) ([]*resources.Resource, error) {
c := cloud.(hetzner.HetznerCloud)
var resourceTrackers []*resources.Resource
sshKeys, err := c.GetSSHKeys(clusterName)
if err != nil {
return nil, fmt.Errorf("failed to list ssh keys: %w", err)
}
for _, sshKey := range sshKeys {
resourceTracker := &resources.Resource{
Name: sshKey.Name,
ID: strconv.FormatInt(sshKey.ID, 10),
Type: resourceTypeSSHKey,
Deleter: deleteSSHKey,
Obj: sshKey,
}
resourceTrackers = append(resourceTrackers, resourceTracker)
}
return resourceTrackers, nil
}
func listNetworks(cloud fi.Cloud, clusterName string) ([]*resources.Resource, error) {View on GitHub (pinned to 4c8573c808)
Solutions
- Verify the API token is set and valid: curl -H "Authorization: Bearer $HCLOUD_TOKEN" https://api.hetzner.cloud/v1/ssh_keys.
- Generate a fresh token in the Hetzner Cloud Console if the old one was revoked or expired.
- Check https://status.hetzner.com for API incidents; retry after transient outages or 429 rate limits.
- Confirm network/proxy access to api.hetzner.cloud from the machine running kops.
Example fix
// before: missing token kops delete cluster --cloud hetzner // after export HCLOUD_TOKEN=<valid-read-write-token> kops delete cluster --cloud hetzner
Defensive patterns
Strategy: retry
Validate before calling
resp, err := http.Get("https://api.hetzner.cloud/v1/ssh_keys")
// (add Authorization header) — verify 200 before running kops
if err != nil || resp.StatusCode != 200 {
return fmt.Errorf("Hetzner API precheck failed (token/network): status=%v err=%v", status, err)
} Type guard
var hErr hcloud.Error
if errors.As(err, &hErr) && hErr.Code == hcloud.ErrorCodeUnauthorized {
// invalid token: prompt for new HCLOUD_TOKEN
} Try / catch
sshKeys, err := c.GetSSHKeys(clusterName)
var hErr hcloud.Error
if errors.As(err, &hErr) {
switch hErr.Code {
case hcloud.ErrorCodeUnauthorized:
// refresh HCLOUD_TOKEN
case hcloud.ErrorCodeRateLimitExceeded:
time.Sleep(backoff) // honor X-Ratelimit-Reset then retry
}
} Prevention
- Always export HCLOUD_TOKEN before Hetzner operations and verify with a cheap GET first.
- Use a project-scoped read/write token created in the Hetzner Cloud Console.
- Check status.hetzner.com before bulk cleanup runs.
- Throttle API calls in CI cleanup jobs to avoid 429s.
When it happens
Trigger: The Hetzner Cloud API GET /ssh_keys request fails: invalid or expired API token, network error, Hetzner API outage, or rate limiting (hcloud rate limits per token).
Common situations: HCLOUD_TOKEN unset or revoked when running kops delete; token lacks read scope; corporate proxy/firewall blocking api.hetzner.cloud; Hetzner maintenance window returning 5xx.
Related errors
- failed to list networks: %w
- failed to list firewalls: %w
- failed to list load balancers: %w
- failed to list servers: %w
- failed to list volumes: %w
AI-assisted analysis of kubernetes/kops@4c8573c808 (2026-09-05).
Data as JSON: /api/errors/a3f5c26971f443c8.
Report an issue: GitHub.