kubernetes/kops · error

error deleting Akamai (Linode) SSH key %s(%s): %w

Error message

error deleting Akamai (Linode) SSH key %s(%s): %w

What it means

deleteSSHKey removes a cluster-generated SSH key from the Linode account. Non-'not found' failures from DeleteSSHKey are wrapped with the key name and ID. linodego.IsNotFound is treated as success (already deleted), so this error means a real API failure while deleting.

Source

Thrown at pkg/resources/linode/resources.go:287

		}
	}

	return resourceTrackers, nil
}

// deleteSSHKey deletes an Akamai (Linode) SSH key.
func deleteSSHKey(cloud fi.Cloud, tracker *resources.Resource) error {
	c := cloud.(cloudlinode.LinodeCloud)
	keyID, err := parseTrackerIntID(tracker)
	if err != nil {
		return err
	}

	if err := c.Client().DeleteSSHKey(context.Background(), keyID); err != nil {
		if linodego.IsNotFound(err) {
			return nil
		}
		return fmt.Errorf("error deleting Akamai (Linode) SSH key %s(%s): %w", tracker.Name, tracker.ID, err)
	}

	return nil
}

// deleteVPC deletes an Akamai (Linode) VPC.
func deleteVPC(cloud fi.Cloud, tracker *resources.Resource) error {
	c := cloud.(cloudlinode.LinodeCloud)
	vpcID, err := parseTrackerIntID(tracker)
	if err != nil {
		return err
	}

	if err := c.Client().DeleteVPC(context.Background(), vpcID); err != nil {
		if linodego.IsNotFound(err) {
			return nil
		}
		return fmt.Errorf("error deleting Akamai (Linode) VPC %s(%s): %w", tracker.Name, tracker.ID, err)

View on GitHub (pinned to 4c8573c808)

Solutions

  1. Use an API token with full read/write scopes for kops delete
  2. Retry the delete; it is idempotent because not-found is tolerated
  3. Check rate limits and back off before retrying
  4. Confirm the key ID exists under account > SSH keys in Linode Cloud Manager
  5. Inspect the wrapped Linode API error status

Example fix

// before
if err := c.Client().DeleteSSHKey(context.Background(), keyID); err != nil {
	if linodego.IsNotFound(err) {
		return nil
	}
	return fmt.Errorf("error deleting Akamai (Linode) SSH key %s(%s): %w", tracker.Name, tracker.ID, err)
}
// after
if err := c.Client().DeleteSSHKey(context.Background(), keyID); err != nil {
	if linodego.IsNotFound(err) {
		return nil
	}
	if linodego.ErrCode(err) == 429 {
		// retry after backoff
	}
	return fmt.Errorf("error deleting Akamai (Linode) SSH key %s(%s): %w", tracker.Name, tracker.ID, err)
}
Defensive patterns

Strategy: retry

Validate before calling

// Ensure token has write scopes before delete operations
curl -sS -H "Authorization: Bearer $LINODE_TOKEN" https://api.linode.com/v4/profile/grants | jq .global

Type guard

func isNotFound(err error) bool { return linodego.IsNotFound(err) }

Try / catch

if err := c.Client().DeleteSSHKey(ctx, keyID); err != nil {
	if linodego.IsNotFound(err) { return nil }
	if linodego.ErrCode(err) == 429 { /* backoff and retry */ }
	return fmt.Errorf("error deleting Akamai (Linode) SSH key %s(%s): %w", tracker.Name, tracker.ID, err)
}

Prevention

When it happens

Trigger: c.Client().DeleteSSHKey(ctx, keyID) fails with 401/403 (insufficient token scopes — write access required), network error, 429 rate limit, or the key ID becoming invalid between listing and deletion.

Common situations: Read-only API token used for cluster deletion; corporate proxy blocking DELETE requests; rate limiting when deleting a large cluster with many resources; concurrent deletion by another operator.

Related errors


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