{"record":{"id":"bffb51b4ec0be217","repo":"gastownhall/beads","slug":"read-response-w","errorCode":null,"errorMessage":"read response: %w","messagePattern":"read response: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/notion/client.go","lineNumber":288,"sourceCode":"\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"create request: %w\", err)\n\t}\n\treq.Header.Set(\"Authorization\", \"Bearer \"+c.Token)\n\treq.Header.Set(\"Notion-Version\", c.NotionVersion)\n\treq.Header.Set(\"Accept\", \"application/json\")\n\tif requestBody != nil {\n\t\treq.Header.Set(\"Content-Type\", \"application/json\")\n\t}\n\n\tresp, err := httpClient.Do(req) //nolint:gosec // G704: URL is constructed from configured Notion API base, not user input\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"request failed: %w\", err)\n\t}\n\tdefer func() { _ = resp.Body.Close() }()\n\n\tbody, err := io.ReadAll(io.LimitReader(resp.Body, maxResponseBytes))\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"read response: %w\", err)\n\t}\n\tif resp.StatusCode >= 200 && resp.StatusCode < 300 {\n\t\treturn body, nil\n\t}\n\n\tvar apiErr struct {\n\t\tCode    string `json:\"code\"`\n\t\tMessage string `json:\"message\"`\n\t}\n\tif err := json.Unmarshal(body, &apiErr); err == nil && apiErr.Message != \"\" {\n\t\treturn nil, fmt.Errorf(\"Notion API error %s (%d): %s\", apiErr.Code, resp.StatusCode, apiErr.Message)\n\t}\n\treturn nil, fmt.Errorf(\"Notion API error (%d): %s\", resp.StatusCode, strings.TrimSpace(string(body)))\n}\n","sourceCodeStart":270,"sourceCodeEnd":303,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/notion/client.go#L270-L303","documentation":"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.","triggerScenarios":"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.","commonSituations":"Unstable network/mobile hotspot dropping long responses; misbehaving corporate proxy truncating and corrupting responses; debugging proxies (custom Transport) with faulty RoundTrippers.","solutions":["Retry the request; this is usually transient (connection reset).","Check for intermediaries (proxy, VPN, MITM) corrupting the response and bypass them.","Inspect the wrapped error: unexpected EOF points at truncation; gzip errors point at encoding issues.","If using a custom http.Client/Transport, test with the default transport to isolate the cause."],"exampleFix":"// before\nresp, err := doWithRetries(0)\nbody, err := readAll(resp) // fails, no retry\n\n// after\nvar body []byte\nfor attempt := 0; attempt < 3; attempt++ {\n    body, err = doAndReadAll()\n    if err == nil {\n        break\n    }\n    time.Sleep(time.Duration(attempt+1) * time.Second)\n}","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"if err != nil && strings.Contains(err.Error(), \"read response\") {\n    // transient; retry the request with backoff\n}","preventionTips":["Retry idempotent GETs on read failures.","Avoid custom Transports unless necessary; test with the default client when debugging.","Watch for proxies/VPN corrupting responses in restricted networks."],"tags":["notion","http","response","io"],"backgroundTag":"response-read-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}