wtfutil/wtf · error

failed to retrieve status: check provided api URL and token

Error message

 failed to retrieve status: check provided api URL and token
 %s

What it means

getStatus in modules/pihole/client.go wraps a json.Unmarshal failure while decoding the Pi-hole /api (status) HTTP response body into the status struct. The library throws it because the response bytes could not be decoded as the expected JSON shape. It is not a network problem — the HTTP call succeeded but the payload was not valid JSON of the expected shape.

Source

Thrown at modules/pihole/client.go:79

	defer func() {
		if closeErr := resp.Body.Close(); closeErr != nil {
			return
		}
	}()

	if resp.StatusCode >= http.StatusBadRequest {
		return status, 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 status, fmt.Errorf(" failed to read status response")
	}

	if err = json.Unmarshal(rBody, &status); err != nil {
		return status, fmt.Errorf(" failed to retrieve status: check provided api URL and token\n %s",
			parseError(err))
	}

	return status, err
}

type FlexInt int

func (fi *FlexInt) UnmarshalJSON(b []byte) error {
	if b[0] != '"' {
		return json.Unmarshal(b, (*int)(fi))
	}

	var s string

	if err := json.Unmarshal(b, &s); err != nil {
		return err
	}

View on GitHub (pinned to bb838c1ccb)

Solutions

  1. Verify settings.apiUrl points to the correct Pi-hole API endpoint (e.g. http://pi.hole/admin/api.php) and that curl on that URL returns JSON.
  2. Check that settings.token matches the webPassword from /etc/pihole/setupVars.conf (or the v6 app password) — an invalid token commonly yields HTML/JSON-auth-error bodies.
  3. Curl the URL with the auth token and inspect the raw body to confirm the JSON schema matches what getStatus unmarshals into.
  4. Confirm you are not being redirected by a reverse proxy to a login page; adjust proxy/API URL accordingly.

Example fix

// before
settings := &Settings{apiUrl: "http://pi.hole/admin/", token: ""}
// after
settings := &Settings{apiUrl: "http://pi.hole/admin/api.php", token: webPasswordFromSetupVars}
Defensive patterns

Strategy: try-catch

Validate before calling

// Go: verify the endpoint returns parseable JSON before use
resp, err := http.Get(settings.apiUrl + "?auth=" + settings.token)
if err == nil {
    var probe map[string]json.RawMessage
    body, _ := io.ReadAll(resp.Body)
    if json.Unmarshal(body, &probe) != nil {
        return errors.New("Pi-hole endpoint did not return valid JSON; check apiUrl and token")
    }
}

Try / catch

if err := client.GetSummaryView(ctx); err != nil {
    if strings.Contains(err.Error(), "failed to retrieve status") {
        // log raw response body, verify token/apiUrl, back off and reconfigure
        log.Printf("Pi-hole status decode failed: %v", err)
        return
    }
    return err
}

Prevention

When it happens

Trigger: The Pi-hole HTTP endpoint returned a 200 response whose body is not JSON matching the expected status struct — e.g. an HTML login/error page, an empty body, or JSON with a different schema than `json.Unmarshal(rBody, &status)` expects in getStatus.

Common situations: Wrong apiUrl pointing at the Pi-hole web root instead of the API path; missing/invalid webpassword token so Pi-hole returns an HTML session page; pointing the client at a reverse proxy or another service; Pi-hole v6 API response schema differing from what this client expects; a proxy or captive portal intercepting the request.

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


AI-assisted analysis of wtfutil/wtf@bb838c1ccb (2026-09-03). Data as JSON: /api/errors/c4fef8b4ed825abc. Report an issue: GitHub.