kubernetes/kops · error
failed to convert ssh key ID %q to int: %s
Error message
failed to convert ssh key ID %q to int: %s
What it means
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.
Source
Thrown at pkg/resources/digitalocean/resources.go:438
resourceTrackers = append(resourceTrackers, &resources.Resource{
Name: key.Name,
ID: strconv.Itoa(key.ID),
Type: resourceTypeSSHKey,
Deleter: deleteSSHKey,
Obj: key,
})
}
return resourceTrackers
}
func deleteSSHKey(cloud fi.Cloud, t *resources.Resource) error {
c := cloud.(do.DOCloud)
id, err := strconv.Atoi(t.ID)
if err != nil {
return fmt.Errorf("failed to convert ssh key ID %q to int: %s", t.ID, err)
}
klog.V(2).Infof("deleting DO SSH key %q (ID %d)", t.Name, id)
response, err := c.KeysService().DeleteByID(context.TODO(), id)
if err != nil {
if response != nil && response.StatusCode == http.StatusNotFound {
return nil
}
return fmt.Errorf("failed to delete ssh key %s (ID %s): %s", t.Name, t.ID, err)
}
return nil
}
func listVPCs(cloud fi.Cloud, clusterName string) ([]*resources.Resource, error) {
c := cloud.(do.DOCloud)
var resourceTrackers []*resources.Resource
View on GitHub (pinned to 4c8573c808)
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)
Example fix
// before
id, err := strconv.Atoi(t.ID)
if err != nil {
return fmt.Errorf("failed to convert ssh key ID %q to int: %s", t.ID, err)
}
// after
id, err := strconv.Atoi(strings.TrimSpace(t.ID))
if err != nil {
return fmt.Errorf("failed to convert ssh key ID %q to int: %v", t.ID, err)
} Defensive patterns
Strategy: validation
Validate before calling
// Validate the ID is numeric before attempting conversion
typedID := strings.TrimSpace(t.ID)
if typedID == "" {
return fmt.Errorf("ssh key resource has empty ID")
}
if _, err := strconv.Atoi(typedID); err != nil {
return fmt.Errorf("ssh key ID %q is not numeric; tracker construction is broken", t.ID)
} Type guard
func isNumericID(s string) bool {
_, err := strconv.Atoi(strings.TrimSpace(s))
return s != "" && err == nil
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- failed to convert droplet ID to int: %s
- failed to list ssh keys: %v
- failed to delete ssh key %s (ID %s): %s
- DIGITALOCEAN_ACCESS_TOKEN is required
- DIGITALOCEAN_ACCESS_TOKEN is required
AI-assisted analysis of kubernetes/kops@4c8573c808 (2026-09-05).
Data as JSON: /api/errors/ccf3b445aecad7f0.
Report an issue: GitHub.