Tencent/WeKnora · error
duckduckgo API returned status %d: %s
Error message
duckduckgo API returned status %d: %s
What it means
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.
Source
Thrown at internal/infrastructure/web_search/duckduckgo.go:161
params.Set("format", "json")
params.Set("no_html", "1")
params.Set("skip_disambig", "1")
reqURL := baseURL + "?" + params.Encode()
req, err := http.NewRequestWithContext(ctx, "GET", reqURL, nil)
if err != nil {
return nil, fmt.Errorf("failed to create request: %w", err)
}
req.Header.Set("User-Agent", "WeKnora/1.0")
resp, err := p.client.Do(req)
if err != nil {
return nil, fmt.Errorf("failed to perform request: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
return nil, fmt.Errorf("duckduckgo API returned status %d: %s", resp.StatusCode, string(body))
}
var apiResponse struct {
AbstractText string `json:"AbstractText"`
AbstractURL string `json:"AbstractURL"`
Heading string `json:"Heading"`
RelatedTopics []struct {
FirstURL string `json:"FirstURL"`
Text string `json:"Text"`
} `json:"RelatedTopics"`
Results []struct {
FirstURL string `json:"FirstURL"`
Text string `json:"Text"`
} `json:"Results"`
}
if err := json.NewDecoder(resp.Body).Decode(&apiResponse); err != nil {
return nil, fmt.Errorf("failed to decode API response: %w", err)
}View on GitHub (pinned to 988cbb0330)
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
Example fix
// before
return nil, fmt.Errorf("duckduckgo API returned status %d: %s", resp.StatusCode, string(body))
// after
if resp.StatusCode == http.StatusTooManyRequests {
return nil, retryableError{fmt.Errorf("duckduckgo rate limited: %d", resp.StatusCode)}
}
return nil, fmt.Errorf("duckduckgo API returned status %d: %s", resp.StatusCode, string(body)) Defensive patterns
Strategy: retry
Validate before calling
if !isRetryableStatus(resp.StatusCode) {
return fmt.Errorf("non-retryable duckduckgo status %d", resp.StatusCode)
}
func isRetryableStatus(code int) bool {
return code == 429 || code >= 500
} Try / catch
results, err := provider.Search(ctx, query, 10, false)
var apiErr *APIStatusError
if errors.As(err, &apiErr) && apiErr.StatusCode == http.StatusTooManyRequests {
time.Sleep(backoff) // then retry or fall back to another provider
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: High search volume triggering rate limits, datacenter IPs blocked by DuckDuckGo, User-Agent 'WeKnora/1.0' being filtered, temporary DuckDuckGo API incidents.
Related errors
- bing API returned status %d: %s
- duckduckgo HTML returned status %d
- HTTP %d for %s
- rate limited: %s
- fetch failed: %w
AI-assisted analysis of Tencent/WeKnora@988cbb0330 (2026-09-02).
Data as JSON: /api/errors/e3f9d7c67c4e5c20.
Report an issue: GitHub.