gastownhall/beads · warning
failed to read response: %w
Error message
failed to read response: %w
What it means
This error is returned when io.ReadAll fails while reading the response body from Linear, limited to MaxResponseSize via io.LimitReader. It is uncommon because reading from an in-memory buffered HTTP body rarely fails; it usually means the connection was reset or dropped mid-read (truncated response).
Source
Thrown at internal/linear/client.go:901
return nil, fmt.Errorf("failed to create request: %w", err)
}
httpReq.Header.Set("Content-Type", "application/json")
authValue, err := c.authHeader()
if err != nil {
return nil, err
}
httpReq.Header.Set("Authorization", authValue)
resp, err := c.HTTPClient.Do(httpReq)
if err != nil {
return nil, fmt.Errorf("request failed: %w", err)
}
respBody, err := io.ReadAll(io.LimitReader(resp.Body, MaxResponseSize))
_ = resp.Body.Close()
if err != nil {
return nil, fmt.Errorf("failed to read response: %w", err)
}
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
return nil, fmt.Errorf("API error: %s (status %d)", string(respBody), resp.StatusCode)
}
var gqlResp struct {
Data json.RawMessage `json:"data"`
Errors []GraphQLError `json:"errors,omitempty"`
}
if err := json.Unmarshal(respBody, &gqlResp); err != nil {
return nil, fmt.Errorf("failed to parse response: %w (body: %s)", err, string(respBody))
}
if len(gqlResp.Errors) > 0 {
errMsgs := make([]string, len(gqlResp.Errors))
for i, e := range gqlResp.Errors {
errMsgs[i] = e.MessageView on GitHub (pinned to 71377f2769)
Solutions
- Retry the request — this is almost always transient
- Check proxy/load-balancer idle timeout settings between the client and api.linear.app
- Log the wrapped error (errors.Unwrap) to confirm it is an unexpected-EOF/reset style failure
- Keep MaxResponseSize reasonable; ensure the caller's network path is stable (avoid aggressive connection reuse through NAT)
Example fix
// before
data, _, err := c.CreateIssueIdempotent(ctx, ...) // transient read failure aborts flow
if err != nil { return err }
// after
var issue *linear.Issue
err := retry.Do(3, backoff, func() error {
var exists bool
issue, exists, err = c.CreateIssueIdempotent(ctx, ...)
return err
}) Defensive patterns
Strategy: retry
Try / catch
if err != nil && strings.Contains(err.Error(), "failed to read response") {
// transient body read failure: safe to retry the whole call
return retryWithBackoff(call)
} Prevention
- Always retry idempotent Linear calls on read failures
- Monitor for connection resets at the proxy/load-balancer layer
- Avoid long-lived keep-alive connections through aggressive NATs
- Keep request/response sizes well under MaxResponseSize
When it happens
Trigger: Connection reset by proxy/load-balancer while streaming the GraphQL response; response aborted after headers were received; TLS truncation; LimitReader boundary interacting with a broken chunked encoding.
Common situations: Flaky networks or mobile/VPN links; intermediaries (corporate proxies, AWS ALB) closing idle connections prematurely; very large error responses being cut off.
Related errors
- reading issue_prefix from hosted database %q: %w
- reading project identity (_project_id) from hosted database
- failed to read response (attempt %d/%d): %w
- failed to read response (attempt %d/%d): %w
- get dependency records: rows: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/58f8c846687ac960.
Report an issue: GitHub.