vxcontrol/pentagi · error · Fatal

failed to decode response body: %w

Error message

failed to decode response body: %w

What it means

The SearxNG response had HTTP 200 but its body could not be JSON-decoded into SearxngResponse. Despite the 200 status, the body was not the expected JSON envelope ({query, results, info}) — usually HTML or empty content. Classified Fatal because a 200 with a non-JSON body will not improve on retry.

Source

Thrown at backend/pkg/tools/searchers/searxng.go:143

		return "", Retryable(fmt.Errorf("failed to do request: %w", err), 0)
	}
	defer resp.Body.Close()

	return s.parseHTTPResponse(resp, query)
}

func (s *searxng) parseHTTPResponse(resp *http.Response, query string) (string, error) {
	if resp.StatusCode != http.StatusOK {
		err := fmt.Errorf("unexpected status code: %d", resp.StatusCode)
		if resp.StatusCode == http.StatusTooManyRequests || resp.StatusCode >= 500 {
			return "", Retryable(err, 0)
		}
		return "", Fatal(err)
	}

	var searxngResponse SearxngResponse
	if err := json.NewDecoder(resp.Body).Decode(&searxngResponse); err != nil {
		return "", Fatal(fmt.Errorf("failed to decode response body: %w", err))
	}

	return s.formatResults(searxngResponse.Results, query), nil
}

func (s *searxng) formatResults(results []SearxngResult, query string) string {
	if len(results) == 0 {
		return fmt.Sprintf("# No Results Found\n\nNo results were found for query: %s", query)
	}

	var builder strings.Builder
	builder.WriteString(fmt.Sprintf("# Searxng Search Results\n\n## Query: %s\n\n", query))
	builder.WriteString("Results from Searxng meta search engine (aggregated from multiple search engines):\n\n")

	for i, result := range results {
		builder.WriteString(fmt.Sprintf("### %d. %s\n\n", i+1, result.Title))

		if result.URL != "" {

View on GitHub (pinned to ea665308ba)

Solutions

  1. Log the first bytes of resp.Body before decoding to see whether the body is HTML or empty
  2. Enable JSON format on the SearxNG instance (search.formats: [html, json]) — some versions serve HTML on 200 when json is disallowed
  3. Check any reverse proxy (nginx/traefik) in front of SearxNG for response rewriting, buffering limits, or content-type interception
  4. Update SearxNG to a current version; old versions may respond unexpectedly to limit/time_range parameters
  5. Pin the Content-Type check: verify the response Content-Type is application/json before decoding and fail with a clearer error otherwise

Example fix

// before
var searxngResponse SearxngResponse
if err := json.NewDecoder(resp.Body).Decode(&searxngResponse); err != nil {
    return "", Fatal(fmt.Errorf("failed to decode response body: %w", err))
}
// after
ct := resp.Header.Get("Content-Type")
if !strings.Contains(ct, "application/json") {
    return "", Fatal(fmt.Errorf("unexpected content type %q from searxng", ct))
}
var searxngResponse SearxngResponse
if err := json.NewDecoder(resp.Body).Decode(&searxngResponse); err != nil {
    return "", Fatal(fmt.Errorf("failed to decode response body: %w", err))
}
Defensive patterns

Strategy: validation

Validate before calling

// validate Content-Type and preview body before decoding
ct := resp.Header.Get("Content-Type")
if !strings.Contains(ct, "application/json") {
    return fmt.Errorf("searxng returned %q instead of JSON", ct)
}
var r SearxngResponse
if err := json.NewDecoder(resp.Body).Decode(&r); err != nil {
    return fmt.Errorf("non-JSON 200 body from searxng: %w", err)
}

Prevention

When it happens

Trigger: SearxNG returning a 200 HTML page (e.g. some instances/proxies render an HTML search page even when format=json is requested, or an error interstitial); empty body from a proxy; response body truncated mid-JSON by a proxy buffer limit.

Common situations: Reverse proxy or WAF in front of SearxNG rewriting responses; SearxNG plugin producing HTML error pages with 200; an older SearxNG version incompatible with the requested parameters that still returns 200 HTML; gzip/encoding issues where a hand-rolled client misses Accept-Encoding handling.

Understand the failure class

Background: "Invalid JSON response" and "Failed to parse response" errors: when an API answers 200 but the body isn't the JSON your library expected — this error's family across 28 libraries.

Related errors


AI-assisted analysis of vxcontrol/pentagi@ea665308ba (2026-09-01). Data as JSON: /api/errors/d44bd443809af6f6. Report an issue: GitHub.