{"record":{"id":"af62f42aceca2fbe","repo":"micro/go-micro","slug":"failed-to-marshal-stream-request-w-af62f4","errorCode":null,"errorMessage":"failed to marshal stream request: %w","messagePattern":"failed to marshal stream request: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"ai/openai/openai.go","lineNumber":211,"sourceCode":"\t}\n\tif req.Prompt != \"\" {\n\t\tmessages = append(messages, map[string]any{\"role\": \"user\", \"content\": req.Prompt})\n\t}\n\tapiReq := map[string]any{\n\t\t\"model\":          p.opts.Model,\n\t\t\"messages\":       messages,\n\t\t\"stream\":         true,\n\t\t\"stream_options\": map[string]any{\"include_usage\": true},\n\t}\n\tif p.opts.MaxTokens > 0 {\n\t\tapiReq[\"max_tokens\"] = p.opts.MaxTokens\n\t}\n\tif p.opts.Effort != \"\" {\n\t\tapiReq[\"reasoning_effort\"] = p.opts.Effort\n\t}\n\treqBody, err := json.Marshal(apiReq)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to marshal stream request: %w\", err)\n\t}\n\tapiURL := strings.TrimRight(p.opts.BaseURL, \"/\") + \"/v1/chat/completions\"\n\thttpReq, err := http.NewRequestWithContext(ctx, http.MethodPost, apiURL, bytes.NewReader(reqBody))\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to create stream request: %w\", err)\n\t}\n\thttpReq.Header.Set(\"Content-Type\", \"application/json\")\n\thttpReq.Header.Set(\"Accept\", \"text/event-stream\")\n\thttpReq.Header.Set(\"Authorization\", \"Bearer \"+p.opts.APIKey)\n\n\thttpResp, err := http.DefaultClient.Do(httpReq)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"stream API request failed: %w\", err)\n\t}\n\tif httpResp.StatusCode != http.StatusOK {\n\t\tdefer httpResp.Body.Close()\n\t\trespBody, _ := io.ReadAll(httpResp.Body)\n\t\treturn nil, fmt.Errorf(\"stream API error (%s): %s\", httpResp.Status, string(respBody))","sourceCodeStart":193,"sourceCodeEnd":229,"githubUrl":"https://github.com/micro/go-micro/blob/24529f140421a11a33b6999ab7944f2021cfd69c/ai/openai/openai.go#L193-L229","documentation":"Thrown in the OpenAI provider's Stream when json.Marshal(apiReq) fails while building the streaming request body. The apiReq is a map[string]any, so marshaling only fails for values that are not JSON-encodable (e.g. channels, funcs, cyclic structures) — rare in practice since options come from typed provider opts.","triggerScenarios":"Calling Stream() when the assembled apiReq map contains a value json.Marshal cannot encode — typically only possible if a caller injected a non-serializable value through custom options or a middleware mutating the request map.","commonSituations":"Custom option injection putting a func or unsupported type into the request map; a wrapping layer building the map dynamically with bad values; almost never hit with standard string/number options.","solutions":["Inspect the apiReq map contents for non-JSON-encodable values","Constrain options to strings, numbers, booleans, and slices/maps thereof","Ensure any middleware copying/deriving the map doesn't inject functions or channels","Log the map before marshaling to identify the offending key"],"exampleFix":"// before\napiReq[\"tools\"] = someFunc // not JSON-encodable\n// after\napiReq[\"tools\"] = toolDefsJSONCompatible","handlingStrategy":"validation","validationCode":"func isJSONSafe(v any) bool {\n    b, err := json.Marshal(v)\n    return err == nil && json.Valid(b)\n}\n// call isJSONSafe(apiReq) before provider.Stream","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Build request maps only from JSON-safe primitives","Avoid injecting funcs/channels into option maps","Add a one-off json.Valid sanity check in tests for request builders"],"tags":["json","serialization","openai","streaming"],"backgroundTag":"json-marshal-failed","analyzedSha":"24529f140421a11a33b6999ab7944f2021cfd69c","analyzedAt":"2026-09-01T02:52:24.923Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}