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

  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.