kubernetes/kops · error

failed to list ssh keys

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.

Solutions

  1. Verify the API token is set and valid: curl -H "Authorization: Bearer $HCLOUD_TOKEN" https://api.hetzner.cloud/v1/ssh_keys.
  2. Generate a fresh token in the Hetzner Cloud Console if the old one was revoked or expired.
  3. Check https://status.hetzner.com for API incidents; retry after transient outages or 429 rate limits.
  4. 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

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


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

Appendix: 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)