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

  1. Inspect how the SSH key resource tracker is built (listSSHKeys/filterClusterSSHKeys) and ensure it stores strconv.Itoa(key.ID)
  2. Check kops get cluster / DO account keys (doctl compute ssh-key list) to confirm the expected numeric ID
  3. If state is corrupted, recreate the resource trackers by re-running discovery rather than hand-editing IDs
  4. 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

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


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