kubernetes/kops · error

droplet metadata returned non-200 status code: %d

Error message

droplet metadata returned non-200 status code: %d

What it means

getMetadata fetches a URL from the DigitalOcean droplet metadata service (169.254.169.254). When the HTTP response status is not 200 OK, it aborts and wraps the status code in this error, meaning the metadata endpoint answered but could not serve the requested value.

Source

Thrown at pkg/nodeidentity/do/identify.go:107

	if accessToken == "" {
		return nil, errors.New("DIGITALOCEAN_ACCESS_TOKEN is required")
	}

	tokenSource := &TokenSource{AccessToken: accessToken}
	oauthClient := oauth2.NewClient(context.TODO(), tokenSource)
	return godo.NewClient(oauthClient), nil
}

func getMetadata(url string) (string, error) {
	resp, err := http.Get(url)
	if err != nil {
		return "", fmt.Errorf("failed to get metadata URL %s: %v", url, err)
	}

	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return "", fmt.Errorf("droplet metadata returned non-200 status code: %d", resp.StatusCode)
	}

	bodyBytes, err := io.ReadAll(resp.Body)
	if err != nil {
		return "", fmt.Errorf("failed to read metadata information %s: %v", url, err)
	}

	return string(bodyBytes), nil
}

// IdentifyNode queries DigitalOcean for the node identity information.
func (i *nodeIdentifier) IdentifyNode(ctx context.Context, node *corev1.Node) (*nodeidentity.Info, error) {
	providerID := node.Spec.ProviderID
	if providerID == "" {
		return nil, errors.New("provider ID cannot be empty")
	}

	const prefix = "digitalocean://"

View on GitHub (pinned to 4c8573c808)

Solutions

  1. Check the exact status code in the error to identify whether it is 404 (bad path/key), 503 (retry later), or 403 (access blocked)
  2. Confirm the workload is actually running on a DigitalOcean droplet that supports the droplet metadata service
  3. Retry the operation; transient 5xx from the metadata service usually resolves
  4. Verify the metadata URL path matches the current DigitalOcean metadata API (e.g. /metadata/v1/region)
  5. Check for network interception of 169.254.169.254 (custom firewalls, link-local routing issues)
Defensive patterns

Strategy: retry

Validate before calling

resp, err := http.Get(metadataURL)
if err != nil { return err }
if resp.StatusCode != http.StatusOK {
    return fmt.Errorf("metadata unavailable: status %d", resp.StatusCode)
}

Try / catch

body, err := getMetadata(url)
if err != nil {
    var netErr net.Error
    if errors.As(err, &netErr) || strings.Contains(err.Error(), "non-200") {
        // retry with backoff or fall back to cached region
    }
}

Prevention

When it happens

Trigger: The metadata service returns 404 (requested metadata key not exposed), 503 (metadata service temporarily unavailable), or 403 on an internal link from within a droplet via getMetadataRegion.

Common situations: Running on a non-DigitalOcean host where the metadata endpoint exists but lacks the key; DigitalOcean metadata service degraded/unavailable; wrong metadata path constructed; proxy or firewall intercepting link-local requests.

Related errors


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