charmbracelet/crush · error
failed to read response body: %w
Error message
failed to read response body: %w
What it means
Wraps an io.ReadAll error while reading a successful (HTTP 200) response body from the Sourcegraph GraphQL API (internal/agent/tools/sourcegraph.go:137-140). The status was OK but the body stream broke mid-read — typically context cancellation, an unexpected EOF from a dropped connection, or an HTTP/2 stream error.
Source
Thrown at internal/agent/tools/sourcegraph.go:139
req.Header.Set("User-Agent", "crush/1.0")
resp, err := client.Do(req)
if err != nil {
return fantasy.ToolResponse{}, fmt.Errorf("failed to fetch URL: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
if len(body) > 0 {
return fantasy.NewTextErrorResponse(fmt.Sprintf("Request failed with status code: %d, response: %s", resp.StatusCode, string(body))), nil
}
return fantasy.NewTextErrorResponse(fmt.Sprintf("Request failed with status code: %d", resp.StatusCode)), nil
}
body, err := io.ReadAll(resp.Body)
if err != nil {
return fantasy.ToolResponse{}, fmt.Errorf("failed to read response body: %w", err)
}
var result map[string]any
if err = json.Unmarshal(body, &result); err != nil {
return fantasy.ToolResponse{}, fmt.Errorf("failed to unmarshal response: %w", err)
}
formattedResults, err := formatSourcegraphResults(result, params.ContextWindow, params.Count)
if err != nil {
return fantasy.NewTextErrorResponse("Failed to format results: " + err.Error()), nil
}
return fantasy.NewTextResponse(formattedResults), nil
},
)
}
func formatSourcegraphResults(result map[string]any, contextWindow, maxResults int) (string, error) {View on GitHub (pinned to 7944b8e522)
Solutions
- Narrow the query (add repo:, file:, or count:) so the response payload is smaller and completes before timeouts.
- Increase the http.Client timeout and/or the tool Timeout param for large searches.
- Retry once on transient errors; check errors.Is(err, context.DeadlineExceeded) to distinguish timeouts from dropped connections.
- Investigate proxy/VPN stability if unexpected EOF recurs on large responses.
Example fix
// before
params := SourcegraphParams{Query: "fmt.Errorf", Timeout: 5}
// after
params := SourcegraphParams{Query: "fmt.Errorf repo:github.com/charmbracelet/crush", Timeout: 60} Defensive patterns
Strategy: retry
Validate before calling
if params.Timeout > 0 && params.Timeout < 15 {
params.Timeout = 15 // avoid cutting large bodies off mid-read
} Type guard
if errors.Is(err, io.ErrUnexpectedEOF) { /* connection dropped mid-body, retryable */ }
if errors.Is(err, context.DeadlineExceeded) { /* body too large for timeout */ } Try / catch
body, err := io.ReadAll(resp.Body)
if err != nil {
if errors.Is(err, context.DeadlineExceeded) || errors.Is(err, io.ErrUnexpectedEOF) {
// retry once with narrower query / longer timeout
}
return err
} Prevention
- Scope Sourcegraph queries with repo:/file: filters to keep response bodies small.
- Use generous timeouts for broad searches on slow links.
- Prefer streaming with a size cap over unbounded ReadAll for very large responses.
- Treat repeated unexpected EOF as a proxy/network stability problem, not a code bug.
When it happens
Trigger: io.ReadAll(resp.Body) errors after a 200 from sourcegraph.com/.api/graphql: the request context deadline fires mid-body, the server or an intermediary closes the connection early (unexpected EOF), or a large result set exceeds what the network can deliver before a timeout.
Common situations: Broad queries (e.g. searching a pattern across all of GitHub) returning huge GraphQL payloads on slow links; flaky VPN/corporate proxies truncating large responses; Timeout param set so low that a big body cannot finish transferring within the client's 30s default.
Related errors
- failed to download from URL: %w
- failed to fetch URL: %w
- failed to fetch URL: %w
- failed to read response: %w
- failed to fetch URL: %w
AI-assisted analysis of charmbracelet/crush@7944b8e522 (2026-08-29).
Data as JSON: /api/errors/e7fdc089b0007233.
Report an issue: GitHub.