charmbracelet/crush · error
search failed with status code: %d
Error message
search failed with status code: %d
What it means
DuckDuckGo returned a non-200, non-202 status (e.g. 403 Forbidden, 429 Too Many Requests, 5xx). The tool treats 202 as rate-limit anomaly challenge; every other non-OK status is rejected with this error since the body won't be a parsable result page. It reflects server-side rejection, not a client bug.
Source
Thrown at internal/agent/tools/search.go:97
return nil, fmt.Errorf("failed to create request: %w", err)
}
setRandomizedHeaders(req)
resp, err := client.Do(req)
if err != nil {
return nil, fmt.Errorf("failed to execute search: %w", err)
}
defer resp.Body.Close()
// A 202 from DuckDuckGo is the anomaly-challenge interstitial, not a
// result page; report throttling rather than parsing it into an
// empty result set.
if resp.StatusCode == http.StatusAccepted {
return nil, errSearchRateLimited
}
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("search failed with status code: %d", resp.StatusCode)
}
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("failed to read response: %w", err)
}
content := string(body)
for _, marker := range ddgAnomalyMarkers {
if strings.Contains(content, marker) {
return nil, errSearchRateLimited
}
}
return parseLiteSearchResults(content, maxResults)
}
func setRandomizedHeaders(req *http.Request) {View on GitHub (pinned to 7944b8e522)
Solutions
- Check the logged status code: 429/403 means back off and slow the request rate
- Add jitter/backoff and cache results to reduce request volume
- Consider routing through a different network/egress if the IP is blocked
- Retry later on 5xx — the failure is server-side
Example fix
// before
for _, q := range queries { search(q) } // tight loop, triggers 403/429
// after
for i, q := range queries {
if i > 0 { time.Sleep(2*time.Second + jitter()) }
search(q)
} Defensive patterns
Strategy: retry
Validate before calling
// after the call, guard before parsing
if resp.StatusCode == http.StatusAccepted { /* rate-limited */ }
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("search unavailable (status %d), backing off", resp.StatusCode)
} Try / catch
if errors.Is(err, errSearchRateLimited) || strings.Contains(err.Error(), "status code: 4") {
select {
case <-time.After(backoffWithJitter()):
return searchDuckDuckGo(ctx, query) // bounded retry
case <-ctx.Done():
return ctx.Err()
}
} Prevention
- Throttle search requests with rate limiting and jitter
- Cache successful results per query
- Treat 403/429 as back-off signals, not bugs
- Fall back to an alternate search backend for critical paths
When it happens
Trigger: searchDuckDuckGo receives resp.StatusCode not in {200, 202} — typically 403 (bot detection/block), 429 (rate limited beyond challenge), or 500-503 (server-side outage).
Common situations: Hammering the endpoint from many requests triggers bot-blocking (403); regional blocks; DuckDuckGo serving maintenance errors; very heavy automated usage from one IP.
Related errors
- failed to create request: %w
- failed to execute search: %w
- could not create request: %w
- unexpected status code: %d
- failed to decode response: %w
AI-assisted analysis of charmbracelet/crush@7944b8e522 (2026-08-29).
Data as JSON: /api/errors/b5d2d51c7f958676.
Report an issue: GitHub.