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.Message

View on GitHub (pinned to 71377f2769)

Solutions

  1. Retry the request — this is almost always transient
  2. Check proxy/load-balancer idle timeout settings between the client and api.linear.app
  3. Log the wrapped error (errors.Unwrap) to confirm it is an unexpected-EOF/reset style failure
  4. 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

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


AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30). Data as JSON: /api/errors/58f8c846687ac960. Report an issue: GitHub.