{"record":{"id":"e3f9d7c67c4e5c20","repo":"Tencent/WeKnora","slug":"duckduckgo-api-returned-status-d-s","errorCode":null,"errorMessage":"duckduckgo API returned status %d: %s","messagePattern":"duckduckgo API returned status (.+?): (.+?)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/infrastructure/web_search/duckduckgo.go","lineNumber":161,"sourceCode":"\tparams.Set(\"format\", \"json\")\n\tparams.Set(\"no_html\", \"1\")\n\tparams.Set(\"skip_disambig\", \"1\")\n\n\treqURL := baseURL + \"?\" + params.Encode()\n\treq, err := http.NewRequestWithContext(ctx, \"GET\", reqURL, nil)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to create request: %w\", err)\n\t}\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 perform request: %w\", err)\n\t}\n\tdefer resp.Body.Close()\n\tif resp.StatusCode != http.StatusOK {\n\t\tbody, _ := io.ReadAll(resp.Body)\n\t\treturn nil, fmt.Errorf(\"duckduckgo API returned status %d: %s\", resp.StatusCode, string(body))\n\t}\n\n\tvar apiResponse struct {\n\t\tAbstractText  string `json:\"AbstractText\"`\n\t\tAbstractURL   string `json:\"AbstractURL\"`\n\t\tHeading       string `json:\"Heading\"`\n\t\tRelatedTopics []struct {\n\t\t\tFirstURL string `json:\"FirstURL\"`\n\t\t\tText     string `json:\"Text\"`\n\t\t} `json:\"RelatedTopics\"`\n\t\tResults []struct {\n\t\t\tFirstURL string `json:\"FirstURL\"`\n\t\t\tText     string `json:\"Text\"`\n\t\t} `json:\"Results\"`\n\t}\n\tif err := json.NewDecoder(resp.Body).Decode(&apiResponse); err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to decode API response: %w\", err)\n\t}","sourceCodeStart":143,"sourceCodeEnd":179,"githubUrl":"https://github.com/Tencent/WeKnora/blob/988cbb03305e055d8ebb7d46d9ac6cc0803cd074/internal/infrastructure/web_search/duckduckgo.go#L143-L179","documentation":"Thrown when the DuckDuckGo Instant Answer API responds with a non-200 status. The response body is read and included in the message so the server's error detail is visible. This means the request succeeded but the API rejected it or is degraded.","triggerScenarios":"Calling Search -> searchAPI when DuckDuckGo returns 4xx/5xx — rate limiting (429), blocked user-agent or IP, API outage (5xx), or upstream redirects the client does not follow.","commonSituations":"High search volume triggering rate limits, datacenter IPs blocked by DuckDuckGo, User-Agent 'WeKnora/1.0' being filtered, temporary DuckDuckGo API incidents.","solutions":["Read the status and body in the error message to identify the cause","Back off and retry on 429/5xx with exponential backoff","Rotate IPs or route through a proxy if the IP is blocked","Set a more realistic browser-like User-Agent if blocked","Check DuckDuckGo API status; fall back to another search provider"],"exampleFix":"// before\nreturn nil, fmt.Errorf(\"duckduckgo API returned status %d: %s\", resp.StatusCode, string(body))\n// after\nif resp.StatusCode == http.StatusTooManyRequests {\n    return nil, retryableError{fmt.Errorf(\"duckduckgo rate limited: %d\", resp.StatusCode)}\n}\nreturn nil, fmt.Errorf(\"duckduckgo API returned status %d: %s\", resp.StatusCode, string(body))","handlingStrategy":"retry","validationCode":"if !isRetryableStatus(resp.StatusCode) {\n    return fmt.Errorf(\"non-retryable duckduckgo status %d\", resp.StatusCode)\n}\nfunc isRetryableStatus(code int) bool {\n    return code == 429 || code >= 500\n}","typeGuard":null,"tryCatchPattern":"results, err := provider.Search(ctx, query, 10, false)\nvar apiErr *APIStatusError\nif errors.As(err, &apiErr) && apiErr.StatusCode == http.StatusTooManyRequests {\n    time.Sleep(backoff) // then retry or fall back to another provider\n}","preventionTips":["Rate-limit outbound searches per tenant/IP","Use a realistic User-Agent to avoid bot filtering","Circuit-break to an alternate provider on repeated non-200s","Alert on elevated 4xx/5xx rates from the search provider"],"tags":["http","api","status-code","rate-limit","go"],"backgroundTag":"api-non-200-response","analyzedSha":"988cbb03305e055d8ebb7d46d9ac6cc0803cd074","analyzedAt":"2026-09-02T14:41:08.344Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}