{"record":{"id":"2dac806aa71dbd0c","repo":"plandex-ai/plandex","slug":"token-exchange-failed-error-reading-body-s","errorCode":null,"errorMessage":"token exchange failed - error reading body: %s","messagePattern":"token exchange failed - error reading body: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"app/cli/lib/claude_max.go","lineNumber":256,"sourceCode":"\t\t\"client_id\":     claudeMaxClientId,\n\t})\n\treq, err := http.NewRequest(\"POST\", claudeMaxTokenUrl, bytes.NewReader(body))\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"token exchange failed - error creating request: %s\", err)\n\t}\n\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, err\n\t}\n\tdefer resp.Body.Close()\n\tif resp.StatusCode != http.StatusOK {\n\t\tb, err := io.ReadAll(resp.Body)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"token exchange failed - error reading body: %s\", err)\n\t\t}\n\t\treturn nil, fmt.Errorf(\"token exchange failed - status: %d, body: %s\", resp.StatusCode, b)\n\t}\n\tvar t types.OauthResponse\n\tif err := json.NewDecoder(resp.Body).Decode(&t); err != nil {\n\t\treturn nil, err\n\t}\n\treturn &t, nil\n}\n\nfunc genCodeVerifier() (string, error) {\n\tbuf := make([]byte, 32)\n\tif _, err := rand.Read(buf); err != nil {\n\t\treturn \"\", err\n\t}\n\treturn base64.RawURLEncoding.EncodeToString(buf), nil\n}\n","sourceCodeStart":238,"sourceCodeEnd":274,"githubUrl":"https://github.com/plandex-ai/plandex/blob/e2d772072efadbe41d2946d97d79be55532dbab5/app/cli/lib/claude_max.go#L238-L274","documentation":"During the Claude Max OAuth code exchange, if the token endpoint responds with a non-200 status, the code attempts to read the response body so it can build a more informative status error. This error is returned only when that io.ReadAll(resp.Body) itself fails.","triggerScenarios":"The token endpoint returned non-200 AND reading the error response body failed — network interruption mid-response, truncated/chunked response, or the server closed the connection before the body was fully received.","commonSituations":"Flaky network or VPN dropping the connection to the token endpoint; proxy terminating the response early; Claude API outage returning an incomplete error response.","solutions":["Retry the token exchange — the failure was in reading the error body, so the root status is unknown and transient network issues are the likely cause.","Check connectivity/proxy settings between the client and claudeMaxTokenUrl.","Log resp.StatusCode even when the body read fails, so the status isn't lost.","Check the Anthropic status page for an ongoing outage."],"exampleFix":"// before\nif resp.StatusCode != http.StatusOK {\n\tb, err := io.ReadAll(resp.Body)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"token exchange failed - error reading body: %s\", err)\n\t}\n// after\nif resp.StatusCode != http.StatusOK {\n\tb, err := io.ReadAll(io.LimitReader(resp.Body, 1<<20))\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"token exchange failed - status: %d, error reading body: %w\", resp.StatusCode, err)\n\t}","handlingStrategy":"retry","validationCode":"client := &http.Client{Timeout: 15 * time.Second}\n// pre-flight reachability\nif resp, err := client.Head(claudeMaxTokenUrl); err != nil { /* warn: endpoint unreachable */ } else { resp.Body.Close() }","typeGuard":"func isBodyReadErr(err error) bool { return strings.Contains(err.Error(), \"error reading body\") }","tryCatchPattern":"for i := 0; i < 3; i++ {\n\ttok, err := exchangeCode(ctx, code, verifier)\n\tif err != nil && strings.Contains(err.Error(), \"error reading body\") {\n\t\ttime.Sleep(time.Duration(1<<i) * time.Second)\n\t\tcontinue\n\t}\n\tbreak\n}","preventionTips":["Set an explicit http.Client timeout instead of DefaultClient.","Retry transient body-read failures with backoff.","Check proxy/VPN stability when exchanging tokens.","Log resp.StatusCode even when body reading fails."],"tags":["go","oauth","http","network","token-exchange"],"backgroundTag":"oauth-token-exchange-failed","analyzedSha":"e2d772072efadbe41d2946d97d79be55532dbab5","analyzedAt":"2026-09-05T20:56:53.631Z","contentChangedAt":"2026-09-05T20:56:53.631Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}