{"record":{"id":"ffea92c3e3cc9fe8","repo":"plandex-ai/plandex","slug":"refresh-failed-read-body-w","errorCode":null,"errorMessage":"refresh failed - read body: %w","messagePattern":"refresh failed - read body: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"app/cli/lib/claude_max.go","lineNumber":316,"sourceCode":"\t}\n\n\treq, err := http.NewRequest(\"POST\", claudeMaxTokenUrl, bytes.NewReader(body))\n\tif err != nil {\n\t\treturn nil, 0, fmt.Errorf(\"refresh failed - create request: %w\", err)\n\t}\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\treq.Header.Set(\"anthropic-beta\", shared.AnthropicClaudeMaxBetaHeader)\n\n\tresp, err := http.DefaultClient.Do(req)\n\tif err != nil {\n\t\treturn nil, 0, fmt.Errorf(\"refresh failed - http: %w\", err)\n\t}\n\tdefer resp.Body.Close()\n\n\tif resp.StatusCode != http.StatusOK {\n\t\tb, err := io.ReadAll(resp.Body)\n\t\tif err != nil {\n\t\t\treturn nil, 0, fmt.Errorf(\"refresh failed - read body: %w\", err)\n\t\t}\n\t\treturn nil, resp.StatusCode, fmt.Errorf(\"refresh failed - status %d: %s\", resp.StatusCode, b)\n\t}\n\n\tvar r types.OauthResponse\n\tif err := json.NewDecoder(resp.Body).Decode(&r); err != nil {\n\t\treturn nil, 0, fmt.Errorf(\"refresh failed - decode: %w\", err)\n\t}\n\n\tnewCreds := &types.OauthCreds{\n\t\tOauthResponse: r,\n\t\tExpiresAt:     time.Now().Add(time.Duration(r.ExpiresIn) * time.Second),\n\t}\n\n\t// persist updated creds\n\taccountCreds.ClaudeMax = newCreds\n\tif err := SetAccountCredentials(accountCreds); err != nil {\n\t\treturn nil, 0, fmt.Errorf(\"refresh failed - save: %w\", err)","sourceCodeStart":298,"sourceCodeEnd":334,"githubUrl":"https://github.com/plandex-ai/plandex/blob/e2d772072efadbe41d2946d97d79be55532dbab5/app/cli/lib/claude_max.go#L298-L334","documentation":"refreshCreds wraps an io.ReadAll failure on the response body returned with a non-200 status from the token endpoint. Because the server already signaled an error status, this usually means the connection was truncated or reset mid-body, so the error text cannot be captured.","triggerScenarios":"resp.StatusCode != 200 and io.ReadAll(resp.Body) returns an error — the server closed the connection early, a proxy/timeout cut the body off, or the response stream is otherwise unreadable.","commonSituations":"Gateway or proxy (403/502 responses) that resets the connection while sending an HTML error page, aggressive server-side timeouts on slow links, or TLS interception appliances killing the stream.","solutions":["Retry the refresh; truncation is typically transient.","Log the status code alongside this error so the actual failure (401/403/5xx) is visible even when the body is lost.","Check for proxies/load balancers with small response timeouts between the client and the token endpoint.","Fall back to io.ReadAtLeast/io.LimitReader if the body is expected to be small and partially readable."],"exampleFix":"// before\nb, err := io.ReadAll(resp.Body)\nif err != nil {\n    return nil, 0, fmt.Errorf(\"refresh failed - read body: %w\", err)\n}\n// after\nb, err := io.ReadAll(io.LimitReader(resp.Body, 1<<16))\nif err != nil {\n    return nil, resp.StatusCode, fmt.Errorf(\"refresh failed - read body (status %d): %w\", resp.StatusCode, err)\n}","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"b, err := io.ReadAll(resp.Body)\nif err != nil {\n    if resp.StatusCode >= 400 {\n        return fmt.Errorf(\"refresh failed (status %d) and body unread: %w\", resp.StatusCode, err)\n    }\n    return retryWithBackoff(refreshCreds, 3) // transient truncation\n}","preventionTips":["Always log the status code when body reads fail so the real error is visible.","Retry truncated reads — they are usually transient network/proxy issues.","Audit intermediate proxies/load balancers for response timeouts."],"tags":["http","network","response-body","truncated"],"backgroundTag":"response-body-read-error","analyzedSha":"e2d772072efadbe41d2946d97d79be55532dbab5","analyzedAt":"2026-09-05T20:56:53.631Z","contentChangedAt":"2026-09-05T20:56:53.631Z","schemaVersion":2},"datasetVersion":"2026-09-12T22:17:10.623Z"}