{"record":{"id":"d7c4599ba92967c9","repo":"VictoriaMetrics/VictoriaMetrics","slug":"cannot-parse-digital-ocean-next-url-s-w","errorCode":null,"errorMessage":"cannot parse digital ocean next url: %s: %w","messagePattern":"cannot parse digital ocean next url: (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"lib/promscrape/discovery/digitalocean/digitalocean.go","lineNumber":119,"sourceCode":"\tLinks    links     `json:\"links\"`\n}\n\ntype links struct {\n\tPages linksPages `json:\"pages\"`\n}\n\ntype linksPages struct {\n\tLast string `json:\"last,omitempty\"`\n\tNext string `json:\"next,omitempty\"`\n}\n\nfunc (r *listDropletResponse) nextURLPath() (string, error) {\n\tif r.Links.Pages.Next == \"\" {\n\t\treturn \"\", nil\n\t}\n\tu, err := url.Parse(r.Links.Pages.Next)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"cannot parse digital ocean next url: %s: %w\", r.Links.Pages.Next, err)\n\t}\n\treturn u.RequestURI(), nil\n}\n\nfunc addDropletLabels(droplets []droplet, defaultPort int) []*promutil.Labels {\n\tvar ms []*promutil.Labels\n\tfor _, droplet := range droplets {\n\t\tif len(droplet.Networks.V4) == 0 {\n\t\t\tcontinue\n\t\t}\n\n\t\tprivateIPv4 := droplet.getIPByNet(\"v4\", \"private\")\n\t\tpublicIPv4 := droplet.getIPByNet(\"v4\", \"public\")\n\t\tpublicIPv6 := droplet.getIPByNet(\"v6\", \"public\")\n\n\t\taddr := discoveryutil.JoinHostPort(publicIPv4, defaultPort)\n\t\tm := promutil.NewLabels(16)\n\t\tm.Add(\"__address__\", addr)","sourceCodeStart":101,"sourceCodeEnd":137,"githubUrl":"https://github.com/VictoriaMetrics/VictoriaMetrics/blob/5079fb58f1e8e62113f90c945ad71586c797d770/lib/promscrape/discovery/digitalocean/digitalocean.go#L101-L137","documentation":"DigitalOcean paginates droplet lists via links.pages.next URL. nextURLPath parses that URL with url.Parse and converts it to a request path; this error fires when the 'next' URL returned by the API is not parseable, including the offending URL in the message.","triggerScenarios":"DigitalOcean API returns a links.pages.next value that is not a valid absolute URL (malformed, empty scheme with bad characters, relative garbage), typically due to an API behavior change or a proxy rewriting the body.","commonSituations":"API version drift or a mock/custom 'server' endpoint emitting handcrafted pagination links; HTML-escaped or truncated JSON from a caching proxy; unexpectedly large droplet counts hitting an edge case in the API's link generation.","solutions":["Log/capture the failing URL from the error message and inspect it","Verify you are on the real DigitalOcean API (custom server may emit bad links)","Check for intermediaries (caching/compressing proxies) altering the JSON body","Retry the discovery — a transiently truncated response can produce a bad link; if persistent, report to DigitalOcean or pin to a known-good refresh window","As a workaround, reduce per-page droplet count or filter targets so pagination isn't triggered"],"exampleFix":"# before (mock server emits bad link)\n{\"links\":{\"pages\":{\"next\":\"https//api.digitalocean.com/v2/droplets?page=2\"}}}\n\n# after\n{\"links\":{\"pages\":{\"next\":\"https://api.digitalocean.com/v2/droplets?page=2\"}}}","handlingStrategy":"retry","validationCode":"# Ensure responses come straight from the real API (no rewriting proxies):\ncurl -fsS -H \"Authorization: Bearer $DO_TOKEN\" 'https://api.digitalocean.com/v2/droplets?per_page=200&page=2' | jq -r '.links.pages.next // empty'\n# If present, it must be an absolute https:// URL","typeGuard":null,"tryCatchPattern":"nextURL, err := apiResp.nextURLPath()\nif err != nil {\n    // log the offending URL embedded in the error, then retry the discovery cycle\n    return nil, err\n}","preventionTips":["Avoid caching/compression proxies that alter JSON bodies on the API path","Use the genuine api.digitalocean.com endpoint for large droplet sets","Keep per-page sizes default so pagination links are generated normally","Retry on transient failures — truncated responses cause bad links"],"tags":["digitalocean","pagination","url","api"],"backgroundTag":"url-parse-failed","analyzedSha":"5079fb58f1e8e62113f90c945ad71586c797d770","analyzedAt":"2026-09-03T18:10:26.153Z","contentChangedAt":"2026-09-03T18:10:26.153Z","schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}