{"record":{"id":"208f134470cd2cad","repo":"chenhg5/cc-connect","slug":"openai-tts-marshal-request-w","errorCode":null,"errorMessage":"openai tts: marshal request: %w","messagePattern":"openai tts: marshal request: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/tts.go","lineNumber":244,"sourceCode":"}\n\n// Synthesize sends text to OpenAI TTS API and returns MP3 audio bytes.\nfunc (o *OpenAITTS) Synthesize(ctx context.Context, text string, opts TTSSynthesisOpts) ([]byte, string, error) {\n\tvoice := opts.Voice\n\tif voice == \"\" {\n\t\tvoice = \"alloy\"\n\t}\n\treqBody := map[string]any{\n\t\t\"model\": o.Model,\n\t\t\"input\": text,\n\t\t\"voice\": voice,\n\t}\n\tif opts.Speed > 0 {\n\t\treqBody[\"speed\"] = opts.Speed\n\t}\n\tjsonData, err := json.Marshal(reqBody)\n\tif err != nil {\n\t\treturn nil, \"\", fmt.Errorf(\"openai tts: marshal request: %w\", err)\n\t}\n\n\turl := strings.TrimRight(o.BaseURL, \"/\") + \"/audio/speech\"\n\treq, err := http.NewRequestWithContext(ctx, http.MethodPost, url, bytes.NewReader(jsonData))\n\tif err != nil {\n\t\treturn nil, \"\", fmt.Errorf(\"openai tts: create request: %w\", err)\n\t}\n\treq.Header.Set(\"Authorization\", \"Bearer \"+o.APIKey)\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\n\tresp, err := o.Client.Do(req)\n\tif err != nil {\n\t\treturn nil, \"\", fmt.Errorf(\"openai tts: request: %w\", err)\n\t}\n\tdefer resp.Body.Close()\n\n\tif resp.StatusCode != http.StatusOK {\n\t\tbody, _ := io.ReadAll(resp.Body)","sourceCodeStart":226,"sourceCodeEnd":262,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/core/tts.go#L226-L262","documentation":"OpenAITTS.Synthesize builds a request body for the OpenAI-compatible /audio/speech endpoint (core/tts.go:244) and marshals it with encoding/json. This error wraps a json.Marshal failure on the map[string]any request body. In practice this is nearly unreachable since all values in the map (strings and a float64) are JSON-marshalable; it exists for defensive completeness.","triggerScenarios":"Only if reqBody contains a value json.Marshal cannot encode — e.g. a future field added to the map holding a func, channel, or other unsupported type. With the current fields (model, input, voice, speed) it cannot occur.","commonSituations":"A developer modifying Synthesize to add an option (e.g. response_format, a nested struct with an unmarshalable field) introduces a non-serializable value into reqBody.","solutions":["Inspect any recently added fields in reqBody for non-JSON-marshalable types (func, chan, complex)","Replace unsupported values with JSON-safe equivalents (strings, numbers, slices, maps, marshalable structs)","If marshaling keeps failing, replace map[string]any with a typed request struct so the compiler catches bad types"],"exampleFix":"// before\nreqBody := map[string]any{\n    \"model\":  o.Model,\n    \"input\":  text,\n    \"voice\":  voice,\n}\n// after\ntype speechRequest struct {\n    Model string  `json:\"model\"`\n    Input string  `json:\"input\"`\n    Voice string  `json:\"voice\"`\n    Speed float64 `json:\"speed,omitempty\"`\n}\nreqBody := speechRequest{Model: o.Model, Input: text, Voice: voice, Speed: opts.Speed}","handlingStrategy":"type-guard","validationCode":"// Prefer a typed struct so the compiler rejects unmarshalable fields\ntype speechRequest struct {\n    Model string  `json:\"model\"`\n    Input string  `json:\"input\"`\n    Voice string  `json:\"voice\"`\n    Speed float64 `json:\"speed,omitempty\"`\n}\nbody := speechRequest{Model: o.Model, Input: text, Voice: voice}\nif _, err := json.Marshal(body); err != nil {\n    return fmt.Errorf(\"request not serializable: %w\", err)\n}","typeGuard":"func jsonSafe(v any) bool {\n    _, err := json.Marshal(v)\n    return err == nil\n}","tryCatchPattern":"audio, format, err := tts.Synthesize(ctx, text, opts)\nif err != nil && strings.Contains(err.Error(), \"marshal request\") {\n    return fmt.Errorf(\"tts request bug (non-serializable field): %w\", err)\n}","preventionTips":["Use a typed request struct instead of map[string]any so bad types fail at compile time","Add a unit test marshaling the request body for every new synthesis option","Never insert func/chan/complex values into the request map"],"tags":["tts","json","serialization","openai"],"backgroundTag":"json-marshal-failed","analyzedSha":"4000b2338aa6e850c99df54f8b0ed6ed7460b401","analyzedAt":"2026-09-06T11:45:09.575Z","contentChangedAt":"2026-09-06T11:45:09.575Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}