{"record":{"id":"5abb1939bb3c8948","repo":"plandex-ai/plandex","slug":"refresh-failed-marshal-w","errorCode":null,"errorMessage":"refresh failed - marshal: %w","messagePattern":"refresh failed - marshal: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"app/cli/lib/claude_max.go","lineNumber":297,"sourceCode":"\nfunc needsRefresh(creds *types.OauthCreds) bool {\n\t// refresh an hour early so we can make multiple calls before it expires\n\treturn time.Now().After(creds.ExpiresAt.Add(-1 * time.Hour))\n}\n\nfunc refreshCreds(accountCreds *types.AccountCredentials) (*types.OauthCreds, int, error) {\n\tcreds := accountCreds.ClaudeMax\n\tif creds == nil {\n\t\treturn nil, 0, fmt.Errorf(\"no stored Claude credentials\")\n\t}\n\n\tbody, err := json.Marshal(map[string]any{\n\t\t\"grant_type\":    \"refresh_token\",\n\t\t\"refresh_token\": creds.RefreshToken,\n\t\t\"client_id\":     claudeMaxClientId,\n\t})\n\tif err != nil {\n\t\treturn nil, 0, fmt.Errorf(\"refresh failed - marshal: %w\", err)\n\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 {","sourceCodeStart":279,"sourceCodeEnd":315,"githubUrl":"https://github.com/plandex-ai/plandex/blob/e2d772072efadbe41d2946d97d79be55532dbab5/app/cli/lib/claude_max.go#L279-L315","documentation":"In refreshCreds, the refresh request body (grant_type=refresh_token, refresh_token, client_id) is built with json.Marshal. This error is returned if marshaling that map fails. The %w wrap preserves the underlying error for errors.Is/As inspection.","triggerScenarios":"json.Marshal of the refresh-token request map fails — in practice only if creds.RefreshToken contains bytes invalid for JSON encoding (e.g. invalid UTF-8 read from a corrupted credentials store).","commonSituations":"A corrupted or hand-edited credentials file put non-UTF-8 bytes into refresh_token; a broken secret store/decoder produced invalid string data.","solutions":["Inspect the underlying error via errors.Unwrap; it pinpoints the invalid value.","Delete the corrupted stored credentials and re-run the OAuth connect flow to store fresh tokens.","Validate that refresh_token is valid UTF-8/ASCII when loading the credentials file.","Avoid hand-editing or binary-transforming the credentials store."],"exampleFix":"// before\nif err != nil {\n\treturn nil, 0, fmt.Errorf(\"refresh failed - marshal: %w\", err)\n}\n// after\nif creds.RefreshToken == \"\" || !utf8.ValidString(creds.RefreshToken) {\n\treturn nil, 0, fmt.Errorf(\"refresh failed - stored refresh token is invalid; re-run OAuth connect\")\n}\nif err != nil {\n\treturn nil, 0, fmt.Errorf(\"refresh failed - marshal: %w\", err)\n}","handlingStrategy":"validation","validationCode":"func validRefreshToken(s string) bool { return s != \"\" && utf8.ValidString(s) }\n// before refresh:\n// if !validRefreshToken(accountCreds.ClaudeMax.RefreshToken) { /* re-run OAuth connect */ }","typeGuard":"func refreshTokenUsable(c *types.OauthCreds) bool { return c != nil && c.RefreshToken != \"\" && utf8.ValidString(c.RefreshToken) }","tryCatchPattern":"out, n, err := refreshCreds(accountCreds)\nif err != nil {\n\tvar marshaled bool\n\tif errors.As(err, new(*json.UnsupportedTypeError)) || errors.As(err, new(*json.UnsupportedValueError)) {\n\t\tmarshaled = true\n\t}\n\tif marshaled { /* credentials corrupted: re-auth */ }\n}","preventionTips":["Load the credentials file through a strict JSON schema parser to catch corruption early.","Never hand-edit or binary-transform the credentials store.","Validate refresh_token is non-empty, valid UTF-8 on load.","Re-run the OAuth connect flow to recover from any stored-credential corruption."],"tags":["go","oauth","json","credentials","refresh-token"],"backgroundTag":"refresh-token-flow-failed","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"}