Tencent/WeKnora · error

searxng returned status %d: %s

Error message

searxng returned status %d: %s

What it means

SearXNG responded with a non-200 HTTP status. The provider reads up to 1024 bytes of the body and includes it in the error so you can see the server's own message (HTML error pages, JSON error payloads, auth failures). Common statuses: 403 when JSON format is disabled, 401 when SearXNG requires auth, 429 for rate limiting, 502/503 for upstream engine failures.

Source

Thrown at internal/infrastructure/web_search/searxng.go:132

	reqURL := p.baseURL + "/search?" + q.Encode()
	logger.Infof(ctx, "[WebSearch][SearXNG] query=%q maxResults=%d url=%s", query, maxResults, p.baseURL)

	req, err := http.NewRequestWithContext(ctx, http.MethodGet, reqURL, nil)
	if err != nil {
		return nil, fmt.Errorf("failed to create request: %w", err)
	}
	req.Header.Set("Accept", "application/json")
	req.Header.Set("User-Agent", "WeKnora/1.0")

	resp, err := p.client.Do(req)
	if err != nil {
		return nil, fmt.Errorf("failed to execute request: %w", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		body, _ := io.ReadAll(io.LimitReader(resp.Body, 1024))
		return nil, fmt.Errorf("searxng returned status %d: %s", resp.StatusCode, string(body))
	}

	var data searxngResponse
	if err := json.NewDecoder(resp.Body).Decode(&data); err != nil {
		p.lastUnresponsive = nil
		return nil, fmt.Errorf("failed to decode SearXNG response (ensure JSON format is enabled in settings.yml): %w", err)
	}
	p.lastUnresponsive = data.UnresponsiveEngines

	results := make([]*types.WebSearchResult, 0, maxResults)
	for _, r := range data.Results {
		if len(results) >= maxResults {
			break
		}
		if r.URL == "" || r.Title == "" {
			continue
		}
		item := &types.WebSearchResult{

View on GitHub (pinned to 988cbb0330)

Solutions

  1. Read the embedded status/body in the error; if 403 with a 'format' message, enable JSON in SearXNG settings.yml (search: formats: [html, json]) and restart SearXNG
  2. If 429, add client-side rate limiting/backoff between Search calls
  3. If 401/403 from a proxy, configure the required auth (basic-auth credentials, token) for the provider or exempt the backend
  4. Check SearXNG logs for the failing request to confirm the server-side reason

Example fix

# before (searxng/settings.yml)
search:
  formats:
    - html
# after
search:
  formats:
    - html
    - json
Defensive patterns

Strategy: fallback

Validate before calling

// verify JSON format enabled before use
r, err := http.Get(baseURL + "/search?q=test&format=json")
if err != nil || r.StatusCode != http.StatusOK {
    return fmt.Errorf("searxng JSON search not available (enable 'json' in search.formats)")
}
r.Body.Close()

Try / catch

var httpErr interface{ HTTPStatus() int }; // or match on embedded status text
if strings.Contains(err.Error(), "status 403") {
    // enable json format in searxng settings.yml
} else if strings.Contains(err.Error(), "status 429") {
    // back off and retry later
}

Prevention

When it happens

Trigger: Any Search() call where the SearXNG instance returns e.g. 403 (Forbidden) because 'format: json' is not enabled in settings.yml, or 429 rate-limit responses under load.

Common situations: Fresh SearXNG install with default settings.yml (search.formats lacks json → 403); reverse proxy auth in front of SearXNG; too many concurrent queries causing 429.

Related errors


AI-assisted analysis of Tencent/WeKnora@988cbb0330 (2026-09-02). Data as JSON: /api/errors/183529ab638aef63. Report an issue: GitHub.