wtfutil/wtf · error
failed to retrieve top clients: check provided api URL and
Error message
failed to retrieve top clients: check provided api URL and token %s
What it means
After reading the response, getTopClients unmarshals the body into TopClients{TopSources map[string]int}. This error is thrown when the body is not valid JSON or does not match that shape. The library deliberately hints that the API URL or token is wrong, because invalid auth usually makes Pi-hole return an HTML error page or a different JSON structure.
Source
Thrown at modules/pihole/client.go:229
defer func() {
if closeErr := resp.Body.Close(); closeErr != nil {
return
}
}()
if resp.StatusCode >= http.StatusBadRequest {
return tc, fmt.Errorf(" failed to retrieve version from Pi-hole server\n http status code: %d",
resp.StatusCode)
}
var rBody []byte
if rBody, err = io.ReadAll(resp.Body); err != nil {
return tc, fmt.Errorf(" failed to read top clients response\n %s", parseError(err))
}
if err = json.Unmarshal(rBody, &tc); err != nil {
return tc, fmt.Errorf(" failed to retrieve top clients: check provided api URL and token\n %s",
parseError(err))
}
return tc, err
}
type QueryTypes struct {
QueryTypes map[string]float32 `json:"querytypes"`
}
func getQueryTypes(c http.Client, settings *Settings) (qt QueryTypes, err error) {
var req *http.Request
var url *url2.URL
if url, err = url2.Parse(settings.apiUrl); err != nil {
return qt, fmt.Errorf(" failed to parse API URL\n %s", parseError(err))
}View on GitHub (pinned to bb838c1ccb)
Solutions
- Print/curl the raw response body to see what Pi-hole actually returned
- Verify the API token is current (Settings > API in the Pi-hole admin UI) and passed as settings.token
- Confirm apiUrl ends at /admin/api.php (the web UI page will return HTML, not JSON)
- If running Pi-hole v6+, check whether the response schema still contains top_sources; upgrade or adapt the client struct if not
- If the token itself appears fine, inspect whether top_sources changed type (e.g. values as floats) and adjust the TopClients struct
Example fix
// before: struct may not match v6 schema
type TopClients struct {
TopSources map[string]int `json:"top_sources"`
}
// after: tolerate float values from newer API
type TopClients struct {
TopSources map[string]float64 `json:"top_sources"`
} Defensive patterns
Strategy: validation
Validate before calling
// pre-flight: confirm the endpoint returns JSON with top_sources
resp, _ := http.Get(settings.apiUrl + "auth=" + settings.token + "&topClients=1")
b, _ := io.ReadAll(resp.Body)
if !json.Valid(b) || !bytes.Contains(b, []byte("top_sources")) {
return fmt.Errorf("unexpected pihole response; check token and api.php URL: %.100s", b)
} Type guard
func looksLikeTopClients(b []byte) bool {
var probe struct {
TopSources map[string]int `json:"top_sources"`
}
return json.Unmarshal(b, &probe) == nil
} Try / catch
tc, err := getTopClients(client, settings)
if err != nil {
if strings.Contains(err.Error(), "check provided api URL and token") {
// body wasn't decodable: dump sanitized body and re-verify token/URL
log.Printf("non-JSON response from %s; verify token", redactedURL)
}
return err
} Prevention
- Validate the token right after generating it with a single curl call
- Never point apiUrl at the admin web UI page; it must end at api.php
- Re-run a schema smoke test after Pi-hole version upgrades
- Log the first ~100 bytes of unexpected bodies (token redacted) to diagnose quickly
When it happens
Trigger: json.Unmarshal fails on the topClients response: Pi-hole returns HTML (login page or error page) instead of JSON, an empty body, or JSON without a top_sources object because the auth token was rejected.
Common situations: Wrong/expired API token causing an HTML response, apiUrl pointing at the web UI instead of api.php, Pi-hole v6 changed the response schema (top_sources shape differs), or a proxy returning an error page.
Understand the failure class
Background: "failed to unmarshal" / json.Unmarshal errors: why parsing a response into a Go struct fails and how to fix it — this error's family across 23 libraries.
Related errors
- failed to retrieve status: check provided api URL and token
- failed to retrieve top items: check provided api URL and to
- azure credentials not initialized for subscription %s: pleas
- decoding response: %w
- failed to marshal request: %v
AI-assisted analysis of wtfutil/wtf@bb838c1ccb (2026-09-03).
Data as JSON: /api/errors/2e4ff87bee252707.
Report an issue: GitHub.