{"record":{"id":"183529ab638aef63","repo":"Tencent/WeKnora","slug":"searxng-returned-status-d-s","errorCode":null,"errorMessage":"searxng returned status %d: %s","messagePattern":"searxng returned status (.+?): (.+?)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/infrastructure/web_search/searxng.go","lineNumber":132,"sourceCode":"\treqURL := p.baseURL + \"/search?\" + q.Encode()\n\tlogger.Infof(ctx, \"[WebSearch][SearXNG] query=%q maxResults=%d url=%s\", query, maxResults, p.baseURL)\n\n\treq, err := http.NewRequestWithContext(ctx, http.MethodGet, reqURL, nil)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to create request: %w\", err)\n\t}\n\treq.Header.Set(\"Accept\", \"application/json\")\n\treq.Header.Set(\"User-Agent\", \"WeKnora/1.0\")\n\n\tresp, err := p.client.Do(req)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to execute request: %w\", err)\n\t}\n\tdefer resp.Body.Close()\n\n\tif resp.StatusCode != http.StatusOK {\n\t\tbody, _ := io.ReadAll(io.LimitReader(resp.Body, 1024))\n\t\treturn nil, fmt.Errorf(\"searxng returned status %d: %s\", resp.StatusCode, string(body))\n\t}\n\n\tvar data searxngResponse\n\tif err := json.NewDecoder(resp.Body).Decode(&data); err != nil {\n\t\tp.lastUnresponsive = nil\n\t\treturn nil, fmt.Errorf(\"failed to decode SearXNG response (ensure JSON format is enabled in settings.yml): %w\", err)\n\t}\n\tp.lastUnresponsive = data.UnresponsiveEngines\n\n\tresults := make([]*types.WebSearchResult, 0, maxResults)\n\tfor _, r := range data.Results {\n\t\tif len(results) >= maxResults {\n\t\t\tbreak\n\t\t}\n\t\tif r.URL == \"\" || r.Title == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\titem := &types.WebSearchResult{","sourceCodeStart":114,"sourceCodeEnd":150,"githubUrl":"https://github.com/Tencent/WeKnora/blob/988cbb03305e055d8ebb7d46d9ac6cc0803cd074/internal/infrastructure/web_search/searxng.go#L114-L150","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["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","If 429, add client-side rate limiting/backoff between Search calls","If 401/403 from a proxy, configure the required auth (basic-auth credentials, token) for the provider or exempt the backend","Check SearXNG logs for the failing request to confirm the server-side reason"],"exampleFix":"# before (searxng/settings.yml)\nsearch:\n  formats:\n    - html\n# after\nsearch:\n  formats:\n    - html\n    - json","handlingStrategy":"fallback","validationCode":"// verify JSON format enabled before use\nr, err := http.Get(baseURL + \"/search?q=test&format=json\")\nif err != nil || r.StatusCode != http.StatusOK {\n    return fmt.Errorf(\"searxng JSON search not available (enable 'json' in search.formats)\")\n}\nr.Body.Close()","typeGuard":null,"tryCatchPattern":"var httpErr interface{ HTTPStatus() int }; // or match on embedded status text\nif strings.Contains(err.Error(), \"status 403\") {\n    // enable json format in searxng settings.yml\n} else if strings.Contains(err.Error(), \"status 429\") {\n    // back off and retry later\n}","preventionTips":["Enable 'json' in search.formats in SearXNG settings.yml at install time","Smoke-test with curl '?format=json' after every SearXNG upgrade (upgrades can reset config)","Add rate limiting on your side to avoid 429","Monitor non-200 statuses and alert on 403 (config regression)"],"tags":["http","http-status","searxng","config","rate-limit"],"backgroundTag":"http-403-forbidden","analyzedSha":"988cbb03305e055d8ebb7d46d9ac6cc0803cd074","analyzedAt":"2026-09-02T14:41:08.344Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}