kubernetes/kops · error
failed to get metadata URL %s: %v
Error message
failed to get metadata URL %s: %v
What it means
getMetadata performs a plain http.Get against the DigitalOcean Droplet metadata endpoint and returns the body. A transport-level failure (connection refused, timeout, no route to the link-local address, DNS) is wrapped as "failed to get metadata URL %s: %v". Non-200 responses produce a separate status-code error, so this one always means the request never completed.
Source
Thrown at pkg/nodeidentity/do/identify.go:101
return getMetadata(dropletRegionMetadataURL)
}
// NewCloud returns a godo client, expecting the env var DIGITALOCEAN_ACCESS_TOKEN to be set.
func NewCloud(region string) (*godo.Client, error) {
accessToken := os.Getenv("DIGITALOCEAN_ACCESS_TOKEN")
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) {View on GitHub (pinned to 4c8573c808)
Solutions
- Confirm you are on a Droplet and curl the exact URL from the error message; if that fails, fix network access first.
- Allow egress to 169.254.169.254 in host/container firewall rules.
- Use host networking for containers (docker --net=host / hostNetwork: true in k8s).
- Check the DO status page for metadata service incidents and add a short retry with backoff for transient failures.
Example fix
// before: containerized agent with bridge networking docker run myorg/kops-do-agent // after: host network so link-local metadata is reachable docker run --net=host myorg/kops-do-agent
Defensive patterns
Strategy: retry
Validate before calling
url := "http://169.254.169.254/metadata/v1/region"
if err := wait.PollImmediate(2*time.Second, 10*time.Second, func() (bool, error) {
resp, err := http.Get(url)
if err != nil {
return false, nil
}
resp.Body.Close()
return resp.StatusCode == http.StatusOK, nil
}); err != nil {
return fmt.Errorf("metadata endpoint %s unreachable", url)
} Try / catch
val, err := getMetadata(url)
if err != nil && strings.Contains(err.Error(), "failed to get metadata URL") {
return retryWithBackoff(3, func() error {
val, err = getMetadata(url)
return err
})
} Prevention
- Verify Droplet metadata reachability at startup with a lightweight probe
- Use host networking for anything needing 169.254.169.254
- Add short retries with backoff for transient metadata failures
- Check DO status page / monitoring for metadata service incidents
When it happens
Trigger: http.Get to http://169.254.169.254/metadata/v1/... fails: not running on a DO Droplet, firewall blocks the link-local metadata address, no route on the interface, or request times out due to metadata service outage.
Common situations: Developer tooling executed off-Droplet; containers without host networking; overly strict nftables/iptables OUTPUT rules; metadata service degraded during DO incidents; VPN or network namespace intercepting 169.254.169.254.
Related errors
- failed to get droplet region: %s
- error loading channel %q: %v
- failed to get private networks from hetzner cloud metadata:
- error reading channel %q: %v
- unhandled LoadBalancer type %q
AI-assisted analysis of kubernetes/kops@4c8573c808 (2026-09-05).
Data as JSON: /api/errors/e463a760252a3562.
Report an issue: GitHub.