charmbracelet/crush · error
failed to read response: %w
Error message
failed to read response: %w
What it means
Wraps an io.ReadAll failure while draining the HTTP response body from the DuckDuckGo Lite endpoint in searchDuckDuckGo (internal/agent/tools/search.go:100-103). The request already succeeded with HTTP 200; only reading the body failed. Common wrapped causes are context cancellation/deadline exceeded mid-read, unexpected EOF from a truncated connection, or a proxy closing the connection early.
Source
Thrown at internal/agent/tools/search.go:102
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) {
req.Header.Set("User-Agent", userAgents[rand.IntN(len(userAgents))])
req.Header.Set("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8")
req.Header.Set("Accept-Language", acceptLanguages[rand.IntN(len(acceptLanguages))])
req.Header.Set("Accept-Encoding", "identity")
req.Header.Set("Connection", "keep-alive")View on GitHub (pinned to 7944b8e522)
Solutions
- Retry the search once with a fresh context; transient read interruptions are usually intermittent.
- Increase the http.Client timeout or the tool-call timeout so the response body can be fully read.
- Check network stability / proxy configuration (HTTP_PROXY, corporate MITM proxies) that can truncate responses.
- If persistent, inspect the wrapped error (%w) for context.Canceled vs unexpected EOF to decide between cancelling logic and network fixes.
Example fix
// before
ctx := context.Background()
results, err := searchDuckDuckGo(ctx, client, query, 10)
// after
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
results, err := searchDuckDuckGo(ctx, client, query, 10)
if err != nil && errors.Is(err, context.DeadlineExceeded) {
// retry once or surface a clearer timeout message
} Defensive patterns
Strategy: retry
Validate before calling
if err := ctx.Err(); err != nil { return nil, fmt.Errorf("search context already cancelled: %w", err) } Type guard
var netErr net.Error
if errors.As(err, &netErr) && netErr.Timeout() { /* treat as retryable timeout */ } Try / catch
results, err := searchDuckDuckGo(ctx, client, query, 10)
if err != nil {
if errors.Is(err, context.DeadlineExceeded) || errors.Is(err, io.ErrUnexpectedEOF) {
// retry once with a longer deadline
}
return err
} Prevention
- Give searches a generous context deadline (>= 30s).
- Avoid routing through unstable proxies or VPNs for outbound HTTP.
- Check ctx.Err() before issuing the request to fail fast on already-cancelled contexts.
- Log the wrapped cause with errors.Is/errors.As rather than the outer message.
When it happens
Trigger: io.ReadAll(resp.Body) errors after a 200 response from lite.duckduckgo.com/lite/: context deadline exceeded (Timeout param or client timeout fires mid-download), server closes the connection before the full HTML is sent (unexpected EOF), or a TLS/HTTP2 stream reset occurs during body transfer.
Common situations: Slow or flaky networks and mobile/VPN connections dropping mid-response; a low http.Client.Timeout that cuts off large result pages; corporate proxies or firewalls that truncate long responses; the search context being cancelled by an upstream tool-call timeout.
Related errors
- failed to read response body: %w
- failed to get config: %w
- failed to set large model: %w
- failed to set small model: %w
- failed to list messages: %w
AI-assisted analysis of charmbracelet/crush@7944b8e522 (2026-08-29).
Data as JSON: /api/errors/1519f34f08fd7762.
Report an issue: GitHub.