{"record":{"id":"ccf3b445aecad7f0","repo":"kubernetes/kops","slug":"failed-to-convert-ssh-key-id-q-to-int-s","errorCode":null,"errorMessage":"failed to convert ssh key ID %q to int: %s","messagePattern":"failed to convert ssh key ID %q to int: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/resources/digitalocean/resources.go","lineNumber":438,"sourceCode":"\n\t\tresourceTrackers = append(resourceTrackers, &resources.Resource{\n\t\t\tName:    key.Name,\n\t\t\tID:      strconv.Itoa(key.ID),\n\t\t\tType:    resourceTypeSSHKey,\n\t\t\tDeleter: deleteSSHKey,\n\t\t\tObj:     key,\n\t\t})\n\t}\n\n\treturn resourceTrackers\n}\n\nfunc deleteSSHKey(cloud fi.Cloud, t *resources.Resource) error {\n\tc := cloud.(do.DOCloud)\n\n\tid, err := strconv.Atoi(t.ID)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to convert ssh key ID %q to int: %s\", t.ID, err)\n\t}\n\n\tklog.V(2).Infof(\"deleting DO SSH key %q (ID %d)\", t.Name, id)\n\tresponse, err := c.KeysService().DeleteByID(context.TODO(), id)\n\tif err != nil {\n\t\tif response != nil && response.StatusCode == http.StatusNotFound {\n\t\t\treturn nil\n\t\t}\n\t\treturn fmt.Errorf(\"failed to delete ssh key %s (ID %s): %s\", t.Name, t.ID, err)\n\t}\n\n\treturn nil\n}\n\nfunc listVPCs(cloud fi.Cloud, clusterName string) ([]*resources.Resource, error) {\n\tc := cloud.(do.DOCloud)\n\tvar resourceTrackers []*resources.Resource\n","sourceCodeStart":420,"sourceCodeEnd":456,"githubUrl":"https://github.com/kubernetes/kops/blob/4c8573c808a73d578c5eadc86d410646ea0b0d73/pkg/resources/digitalocean/resources.go#L420-L456","documentation":"deleteSSHKey calls strconv.Atoi on the resource tracker's string ID before deleting a DigitalOcean SSH key. This error means the stored SSH key resource ID is not a plain decimal integer, so the delete API cannot be called. It is a local data-conversion failure, not a DO API error.","triggerScenarios":"t.ID is empty or non-numeric — e.g. the resource tracker was constructed with a name/ fingerprint instead of the numeric key ID, or the ID was populated from a malformed source.","commonSituations":"Custom code or tooling creating DO resource trackers with string identifiers; corrupted/edited cluster state; a DO API version change altering the ID field semantics used when building trackers.","solutions":["Inspect how the SSH key resource tracker is built (listSSHKeys/filterClusterSSHKeys) and ensure it stores strconv.Itoa(key.ID)","Check kops get cluster / DO account keys (doctl compute ssh-key list) to confirm the expected numeric ID","If state is corrupted, recreate the resource trackers by re-running discovery rather than hand-editing IDs","Use %v not %s for the wrapped err in the message so the conversion detail is visible (minor, cosmetic)"],"exampleFix":"// before\nid, err := strconv.Atoi(t.ID)\nif err != nil {\n\treturn fmt.Errorf(\"failed to convert ssh key ID %q to int: %s\", t.ID, err)\n}\n// after\nid, err := strconv.Atoi(strings.TrimSpace(t.ID))\nif err != nil {\n\treturn fmt.Errorf(\"failed to convert ssh key ID %q to int: %v\", t.ID, err)\n}","handlingStrategy":"validation","validationCode":"// Validate the ID is numeric before attempting conversion\ntypedID := strings.TrimSpace(t.ID)\nif typedID == \"\" {\n\treturn fmt.Errorf(\"ssh key resource has empty ID\")\n}\nif _, err := strconv.Atoi(typedID); err != nil {\n\treturn fmt.Errorf(\"ssh key ID %q is not numeric; tracker construction is broken\", t.ID)\n}","typeGuard":"func isNumericID(s string) bool {\n\t_, err := strconv.Atoi(strings.TrimSpace(s))\n\treturn s != \"\" && err == nil\n}","tryCatchPattern":null,"preventionTips":["Always populate DO resource tracker IDs from the numeric godo key ID (strconv.Itoa(key.ID))","Never overwrite resource tracker IDs with names or fingerprints","Add a unit test asserting tracker IDs parse as integers before delete","Validate cluster state after external tooling touches it"],"tags":["digitalocean","ssh","type-conversion","data-integrity"],"backgroundTag":"invalid-id-format","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"}