{"record":{"id":"252f4b2ffcf8c10f","repo":"plandex-ai/plandex","slug":"refresh-failed-decode-w","errorCode":null,"errorMessage":"refresh failed - decode: %w","messagePattern":"refresh failed - decode: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"app/cli/lib/claude_max.go","lineNumber":323,"sourceCode":"\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)\n\t}\n\n\treturn newCreds, resp.StatusCode, nil\n}\n","sourceCodeStart":305,"sourceCodeEnd":339,"githubUrl":"https://github.com/plandex-ai/plandex/blob/e2d772072efadbe41d2946d97d79be55532dbab5/app/cli/lib/claude_max.go#L305-L339","documentation":"refreshCreds wraps a json.Decoder failure while parsing the token endpoint's 200 response into types.OauthResponse. A 200 was received, but the body is not the expected JSON shape — the response was truncated, is HTML from an intercepting proxy, or the API contract changed.","triggerScenarios":"json.NewDecoder(resp.Body).Decode(&r) errors on a 200 response: empty body, HTML/text error page disguised as 200, truncated JSON, or new/renamed fields breaking types.OauthResponse unmarshaling.","commonSituations":"Corporate proxies or captive portals returning HTML with status 200, server bug returning an empty 200, API version change altering the OauthResponse schema, or gzip/encoding mishandling.","solutions":["Capture and log the raw body (read it fully first, then json.Unmarshal) so you can see what was actually returned.","Check for a captive portal or proxy injecting HTML; bypass the proxy for the token host.","Verify the client/API version pairing — update types.OauthResponse if the token endpoint schema changed.","Retry on transient truncation; add Content-Length validation before decoding."],"exampleFix":"// before\nvar r types.OauthResponse\nif err := json.NewDecoder(resp.Body).Decode(&r); err != nil {\n    return nil, 0, fmt.Errorf(\"refresh failed - decode: %w\", err)\n}\n// after\nb, _ := io.ReadAll(io.LimitReader(resp.Body, 1<<20))\nvar r types.OauthResponse\nif err := json.Unmarshal(b, &r); err != nil {\n    return nil, 0, fmt.Errorf(\"refresh failed - decode: %w; body=%q\", err, string(b))\n}\nif r.RefreshToken == \"\" {\n    return nil, 0, fmt.Errorf(\"refresh failed - missing refresh_token in response\")\n}","handlingStrategy":"type-guard","validationCode":"b, err := io.ReadAll(io.LimitReader(resp.Body, 1<<20))\nif err != nil {\n    return fmt.Errorf(\"cannot read 200 response body: %w\", err)\n}\nct := resp.Header.Get(\"Content-Type\")\nif !strings.Contains(ct, \"application/json\") {\n    return fmt.Errorf(\"unexpected content type %q from token endpoint\", ct)\n}","typeGuard":"func validOauthResponse(r *types.OauthResponse) bool {\n    return r != nil && r.AccessToken != \"\" && r.RefreshToken != \"\" && r.ExpiresIn > 0\n}","tryCatchPattern":"var r types.OauthResponse\nif err := json.Unmarshal(b, &r); err != nil {\n    log.Printf(\"token endpoint returned non-JSON 200 body: %q\", string(b))\n    return fmt.Errorf(\"refresh failed - decode: %w\", err)\n}\nif !validOauthResponse(&r) {\n    return fmt.Errorf(\"refresh failed - incomplete oauth response\")\n}","preventionTips":["Log raw response bodies on decode failure for diagnosis.","Validate required fields (access_token, refresh_token, expires_in) after decoding.","Check Content-Type before decoding JSON.","Keep types.OauthResponse in sync with the API schema on version upgrades."],"tags":["json","decoding","oauth","schema"],"backgroundTag":"json-decode-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"}