gastownhall/beads · error

read response: %w

Error message

read response: %w

What it means

Reading the Notion response body failed via io.ReadAll over an io.LimitReader capped at maxResponseBytes. This is uncommon and usually indicates the connection dropped mid-response, decompression failure, or a custom http.Client Transport returning a broken body reader.

Source

Thrown at internal/notion/client.go:288

	if err != nil {
		return nil, fmt.Errorf("create request: %w", err)
	}
	req.Header.Set("Authorization", "Bearer "+c.Token)
	req.Header.Set("Notion-Version", c.NotionVersion)
	req.Header.Set("Accept", "application/json")
	if requestBody != nil {
		req.Header.Set("Content-Type", "application/json")
	}

	resp, err := httpClient.Do(req) //nolint:gosec // G704: URL is constructed from configured Notion API base, not user input
	if err != nil {
		return nil, fmt.Errorf("request failed: %w", err)
	}
	defer func() { _ = resp.Body.Close() }()

	body, err := io.ReadAll(io.LimitReader(resp.Body, maxResponseBytes))
	if err != nil {
		return nil, fmt.Errorf("read response: %w", err)
	}
	if resp.StatusCode >= 200 && resp.StatusCode < 300 {
		return body, nil
	}

	var apiErr struct {
		Code    string `json:"code"`
		Message string `json:"message"`
	}
	if err := json.Unmarshal(body, &apiErr); err == nil && apiErr.Message != "" {
		return nil, fmt.Errorf("Notion API error %s (%d): %s", apiErr.Code, resp.StatusCode, apiErr.Message)
	}
	return nil, fmt.Errorf("Notion API error (%d): %s", resp.StatusCode, strings.TrimSpace(string(body)))
}

View on GitHub (pinned to 71377f2769)

Solutions

  1. Retry the request; this is usually transient (connection reset).
  2. Check for intermediaries (proxy, VPN, MITM) corrupting the response and bypass them.
  3. Inspect the wrapped error: unexpected EOF points at truncation; gzip errors point at encoding issues.
  4. If using a custom http.Client/Transport, test with the default transport to isolate the cause.

Example fix

// before
resp, err := doWithRetries(0)
body, err := readAll(resp) // fails, no retry

// after
var body []byte
for attempt := 0; attempt < 3; attempt++ {
    body, err = doAndReadAll()
    if err == nil {
        break
    }
    time.Sleep(time.Duration(attempt+1) * time.Second)
}
Defensive patterns

Strategy: retry

Try / catch

if err != nil && strings.Contains(err.Error(), "read response") {
    // transient; retry the request with backoff
}

Prevention

When it happens

Trigger: Connection reset while streaming the response body; gzip/deflate decode error when the transport is set to transparently decompress and the server/intermediary sends corrupt data; a custom Transport whose body reader returns a non-EOF error; the read error also fires if the response exceeds available resources in constrained environments.

Common situations: Unstable network/mobile hotspot dropping long responses; misbehaving corporate proxy truncating and corrupting responses; debugging proxies (custom Transport) with faulty RoundTrippers.

Related errors


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