{"record":{"id":"90e96b138628ea4a","repo":"charmbracelet/crush","slug":"marshal-request-w","errorCode":null,"errorMessage":"marshal request: %w","messagePattern":"marshal request: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"internal/oauth/hyper/device.go","lineNumber":160,"sourceCode":"\t\treturn result, fmt.Errorf(\"unmarshal response: %w: %s\", err, string(body))\n\t}\n\n\tif resp.StatusCode != http.StatusOK {\n\t\treturn result, fmt.Errorf(\"token request failed: status %d body %q\", resp.StatusCode, string(body))\n\t}\n\n\treturn result, nil\n}\n\n// ExchangeToken exchanges a refresh token for an access token.\nfunc ExchangeToken(ctx context.Context, refreshToken string) (*oauth.Token, error) {\n\treqBody := map[string]string{\n\t\t\"refresh_token\": refreshToken,\n\t}\n\n\tdata, err := json.Marshal(reqBody)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"marshal request: %w\", err)\n\t}\n\n\turl := hyper.BaseURL() + \"/token/exchange\"\n\treq, err := http.NewRequestWithContext(ctx, http.MethodPost, url, bytes.NewReader(data))\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"create request: %w\", err)\n\t}\n\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\treq.Header.Set(\"User-Agent\", \"crush\")\n\n\tclient := &http.Client{Timeout: 30 * time.Second}\n\tresp, err := client.Do(req)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"execute request: %w\", err)\n\t}\n\tdefer resp.Body.Close()\n","sourceCodeStart":142,"sourceCodeEnd":178,"githubUrl":"https://github.com/charmbracelet/crush/blob/7944b8e52225d8805e31eacbf7ef24856b0dfb7a/internal/oauth/hyper/device.go#L142-L178","documentation":"This error wraps a failure of json.Marshal when serializing the {\"refresh_token\": ...} request body inside ExchangeToken. With a map[string]string containing a single string value, marshalling essentially cannot fail in practice, so hitting this error indicates an extraordinary encoding-layer fault rather than bad input. It is a defensive wrapper for consistency with the package's other error paths.","triggerScenarios":"json.Marshal(reqBody) returns an error while encoding the refresh-token map in ExchangeToken (called by loginHyper, exchange, and an anonymous caller). Given the input is always a valid map[string]string, a trigger would require a corrupted encoding environment (e.g. custom json.MarshalOverride / patched encoding/json) or a code change introducing a non-marshalable value into the request body.","commonSituations":"Practically never seen in the field; most likely encountered after local modifications to ExchangeToken that add channels/funcs/cyclic structures to the request body, or a broken custom JSON encoder injected in tests.","solutions":["Treat it as a bug: if it reproduces with unmodified code, file an issue with the full wrapped error and Go version","Check for local patches or test hooks overriding encoding/json behavior","If you modified the request body to include non-marshalable types (channels, funcs, cyclic pointers), fix or remove them","Ensure the refreshToken value contains no exotic types (it must be a plain string) and that callers pass a normal string"],"exampleFix":"// before: marshalling a dynamic map that could theoretically hold non-marshalable values\nreqBody := map[string]string{\n    \"refresh_token\": refreshToken,\n}\ndata, err := json.Marshal(reqBody)\nif err != nil {\n    return nil, fmt.Errorf(\"marshal request: %w\", err)\n}\n// after: a fixed struct makes marshal failure provably impossible\nreqBody := struct {\n    RefreshToken string `json:\"refresh_token\"`\n}{RefreshToken: refreshToken}\ndata, err := json.Marshal(reqBody)\nif err != nil {\n    return nil, fmt.Errorf(\"marshal request: %w\", err)\n}","handlingStrategy":"type-guard","validationCode":"// json.Marshal of a plain string cannot fail, but guard the input anyway:\nfunc validateRefreshToken(refreshToken string) error {\n    if strings.TrimSpace(refreshToken) == \"\" {\n        return errors.New(\"refresh token is empty\")\n    }\n    return nil\n}\n// call before ExchangeToken:\nif err := validateRefreshToken(refreshToken); err != nil {\n    return nil, err\n}","typeGuard":"func isMarshalError(err error) bool {\n    var jsonErr *json.UnsupportedTypeError\n    return err != nil && (strings.HasPrefix(err.Error(), \"marshal request:\") || errors.As(err, &jsonErr))\n}","tryCatchPattern":"token, err := hyper.ExchangeToken(ctx, refreshToken)\nif err != nil {\n    if strings.HasPrefix(err.Error(), \"marshal request:\") {\n        // Practically unreachable with a plain string map; treat as a bug.\n        return nil, fmt.Errorf(\"unexpected request encoding failure: %w\", err)\n    }\n    return nil, err\n}","preventionTips":["Always pass a plain, non-empty string as the refresh token","If extending the request body, only add marshalable types (strings, structs)","Use a fixed struct instead of a map for request bodies to catch issues at compile time","Treat any occurrence of this error in unmodified code as a bug and report it"],"tags":["json","marshal","serialization","defensive"],"backgroundTag":"json-marshal-failed","analyzedSha":"7944b8e52225d8805e31eacbf7ef24856b0dfb7a","analyzedAt":"2026-08-29T12:48:59.079Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}