kubernetes/kops · error

failed to list domains: %s

Error message

failed to list domains: %s

What it means

Wraps any error from the DigitalOcean godo DomainService().List API call during DNS resource discovery in kops' digitalocean destroy/listDns flow. It indicates the cloud API listing of domains failed before kOps could match the cluster name to a domain. The underlying DO API error is embedded in the message.

Source

Thrown at pkg/resources/digitalocean/resources.go:145

			var blocks []string
			for _, dropletID := range volume.DropletIDs {
				blocks = append(blocks, "droplet:"+strconv.Itoa(dropletID))
			}

			resourceTracker.Blocks = blocks
			resourceTrackers = append(resourceTrackers, resourceTracker)
		}
	}

	return resourceTrackers, nil
}

func listDNS(cloud fi.Cloud, clusterName string) ([]*resources.Resource, error) {
	c := cloud.(do.DOCloud)
	domains, _, err := c.DomainService().List(context.TODO(), &godo.ListOptions{})
	if err != nil {
		return nil, fmt.Errorf("failed to list domains: %s", err)
	}

	var domainName string
	for _, domain := range domains {
		if strings.HasSuffix(clusterName, domain.Name) {
			domainName = domain.Name
		}
	}

	if domainName == "" {
		return nil, fmt.Errorf("failed to find domain for cluster: %s", clusterName)
	}

	records, err := getAllRecordsByDomain(c, domainName)
	if err != nil {
		return nil, fmt.Errorf("failed to list records for domain %s: %s", domainName, err)
	}

View on GitHub (pinned to 4c8573c808)

Solutions

  1. Verify the DO API token is valid and has domain read scope (doctl auth init / check DIGITALOCEAN_ACCESS_TOKEN)
  2. Retry the operation; transient API/network failures are common
  3. Check https://www.digitaloceanstatus.com for API outages
  4. Inspect the wrapped error in the message for 401/403/429 and address accordingly

Example fix

// before
kops delete cluster --name my.cluster --cloud digitalocean  # fails with API error
// after
export DIGITALOCEAN_ACCESS_TOKEN=<valid token with domain read scope>
kops delete cluster --name my.cluster --cloud digitalocean
Defensive patterns

Strategy: try-catch

Validate before calling

func validateDOToken(token string) error {
	if token == "" { return errors.New("DIGITALOCEAN_ACCESS_TOKEN not set") }
	client := godo.NewFromToken(token)
	_, _, err := client.Domains.List(context.TODO(), &godo.ListOptions{PerPage: 1})
	return err
}

Type guard

func isAuthError(err error) bool {
	return strings.Contains(err.Error(), "401") || strings.Contains(err.Error(), "403")
}

Try / catch

domains, _, err := c.DomainService().List(ctx, &godo.ListOptions{})
if err != nil {
	if isRateLimit(err) { /* backoff and retry */ }
	return fmt.Errorf("failed to list domains: %s", err)
}

Prevention

When it happens

Trigger: c.DomainService().List(context.TODO(), &godo.ListOptions{}) returns an error: invalid/expired DO API token, network failure, DO API outage, or rate limiting (429).

Common situations: Expired or misscoped DIGITALOCEAN_ACCESS_TOKEN (token lacking 'read' scope on domains), corporate proxy blocking api.digitalocean.com, or transient DO API 5xx during cluster deletion.

Related errors


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