wtfutil/wtf · error

failed to parse query types response %s

Error message

 failed to parse query types response
 %s

What it means

This error comes from the Pi-hole widget's getQueryTypes function when the HTTP response body from the Pi-hole API cannot be unmarshaled into the query-types data structure. The library fetched the top/query-types endpoint successfully but the JSON payload did not match the expected shape. parseError wraps the underlying json.Unmarshal error with source context.

Source

Thrown at modules/pihole/client.go:288

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

	if resp.StatusCode >= http.StatusBadRequest {
		return qt, 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 qt, fmt.Errorf(" failed to read top clients response\n %s", parseError(err))
	}

	if err = json.Unmarshal(rBody, &qt); err != nil {
		return qt, fmt.Errorf(" failed to parse query types response\n %s", parseError(err))
	}

	return qt, err
}

func checkServer(c http.Client, apiURL string) error {
	var err error

	var req *http.Request

	var url *url2.URL

	if url, err = url2.Parse(apiURL); err != nil {
		return fmt.Errorf(" failed to parse API URL\n %s", parseError(err))
	}

	if url.Host == "" {
		return fmt.Errorf(" please specify 'apiUrl' in Pi-hole settings, e.g.\n apiUrl: http://<server>:<port>/admin/api.php")

View on GitHub (pinned to bb838c1ccb)

Solutions

  1. Verify the Pi-hole API version is 3-compatible and the apiUrl points to the correct api.php endpoint
  2. Confirm auth settings (API token/webpassword) are correct so Pi-hole returns JSON, not an auth/HTML page
  3. Curl the query types endpoint manually (apiUrl?getQueryTypes) and inspect the raw response for HTML or schema drift
  4. Update the widget/library if your Pi-hole version changed its JSON schema

Example fix

// before: apiUrl pointing at a dashboard URL returning HTML
apiUrl: http://pi.hole/admin/
// after: apiUrl pointing at the api.php endpoint
apiUrl: http://pi.hole/admin/api.php
Defensive patterns

Strategy: validation

Validate before calling

resp, err := http.Get(apiURL + "?getQueryTypes")
if err == nil {
    var probe struct{ QueryTypes map[string]int `json:"query_types"` }
    if jsonErr := json.NewDecoder(resp.Body).Decode(&probe); jsonErr != nil {
        // config/auth problem: response is not expected JSON
    }
}

Try / catch

qt, err := widget.getQueryTypes()
if err != nil {
    log.Printf("query types unavailable, skipping panel: %v", err)
    return
}

Prevention

When it happens

Trigger: io.ReadAll succeeded but json.Unmarshal(rBody, &qt) failed while parsing the query types endpoint response in getQueryTypes (called from getTopClientsView).

Common situations: Pi-hole returns an HTML login page instead of JSON (missing/expired auth token), the apiUrl points at Pi-hole v5/v6 whose response schema differs, a proxy or captive portal returns an error page, or the response is empty/truncated.

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/754dded0bf248bf7. Report an issue: GitHub.