{"record":{"id":"a3f5c26971f443c8","repo":"kubernetes/kops","slug":"failed-to-list-ssh-keys-w","errorCode":null,"errorMessage":"failed to list ssh keys: %w","messagePattern":"failed to list ssh keys: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/resources/hetzner/resources.go","lineNumber":76,"sourceCode":"\t\trt, err := fn(cloud, clusterName)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tfor _, t := range rt {\n\t\t\tresourceTrackers[t.Type+\":\"+t.ID] = t\n\t\t}\n\t}\n\n\treturn resourceTrackers, nil\n}\n\nfunc listSSHKeys(cloud fi.Cloud, clusterName string) ([]*resources.Resource, error) {\n\tc := cloud.(hetzner.HetznerCloud)\n\tvar resourceTrackers []*resources.Resource\n\n\tsshKeys, err := c.GetSSHKeys(clusterName)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to list ssh keys: %w\", err)\n\t}\n\n\tfor _, sshKey := range sshKeys {\n\t\tresourceTracker := &resources.Resource{\n\t\t\tName:    sshKey.Name,\n\t\t\tID:      strconv.FormatInt(sshKey.ID, 10),\n\t\t\tType:    resourceTypeSSHKey,\n\t\t\tDeleter: deleteSSHKey,\n\t\t\tObj:     sshKey,\n\t\t}\n\n\t\tresourceTrackers = append(resourceTrackers, resourceTracker)\n\t}\n\n\treturn resourceTrackers, nil\n}\n\nfunc listNetworks(cloud fi.Cloud, clusterName string) ([]*resources.Resource, error) {","sourceCodeStart":58,"sourceCodeEnd":94,"githubUrl":"https://github.com/kubernetes/kops/blob/4c8573c808a73d578c5eadc86d410646ea0b0d73/pkg/resources/hetzner/resources.go#L58-L94","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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."],"exampleFix":"// before: missing token\nkops delete cluster --cloud hetzner\n// after\nexport HCLOUD_TOKEN=<valid-read-write-token>\nkops delete cluster --cloud hetzner","handlingStrategy":"retry","validationCode":"resp, err := http.Get(\"https://api.hetzner.cloud/v1/ssh_keys\")\n// (add Authorization header) — verify 200 before running kops\nif err != nil || resp.StatusCode != 200 {\n    return fmt.Errorf(\"Hetzner API precheck failed (token/network): status=%v err=%v\", status, err)\n}","typeGuard":"var hErr hcloud.Error\nif errors.As(err, &hErr) && hErr.Code == hcloud.ErrorCodeUnauthorized {\n    // invalid token: prompt for new HCLOUD_TOKEN\n}","tryCatchPattern":"sshKeys, err := c.GetSSHKeys(clusterName)\nvar hErr hcloud.Error\nif errors.As(err, &hErr) {\n    switch hErr.Code {\n    case hcloud.ErrorCodeUnauthorized:\n        // refresh HCLOUD_TOKEN\n    case hcloud.ErrorCodeRateLimitExceeded:\n        time.Sleep(backoff) // honor X-Ratelimit-Reset then retry\n    }\n}","preventionTips":["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."],"tags":["hetzner","ssh","api","cloud-provider"],"backgroundTag":"cloud-api-auth-failure","analyzedSha":"4c8573c808a73d578c5eadc86d410646ea0b0d73","analyzedAt":"2026-09-05T04:13:19.212Z","contentChangedAt":"2026-09-05T04:13:19.212Z","schemaVersion":2},"datasetVersion":"2026-09-12T07:17:12.445Z"}