{"record":{"id":"7e493cd0bee5d5a6","repo":"Billionmail/BillionMail","slug":"marshal-tts-request-w","errorCode":null,"errorMessage":"marshal TTS request: %w","messagePattern":"marshal TTS request: %w","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"core/internal/service/video_gen/voice.go","lineNumber":118,"sourceCode":"\t}\n\n\thttpReq, err := http.NewRequest(\"POST\", cfg.voiceBaseURL()+cartesiaClonePath, bytes.NewReader(body))\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"create clone request: %w\", err)\n\t}\n\n\thttpReq.Header.Set(\"X-API-Key\", cfg.APIKey)\n\thttpReq.Header.Set(\"Cartesia-Version\", cartesiaAPIVersion)\n\thttpReq.Header.Set(\"Content-Type\", \"application/json\")\n\treturn httpReq, nil\n}\n\n// BuildTTSRequest constructs the HTTP request for text-to-speech.\n// Exported for testing without making API calls.\nfunc BuildTTSRequest(cfg VoiceConfig, req TTSRequest) (*http.Request, error) {\n\tbody, err := json.Marshal(req)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"marshal TTS request: %w\", err)\n\t}\n\n\thttpReq, err := http.NewRequest(\"POST\", cfg.voiceBaseURL()+cartesiaTTSPath, bytes.NewReader(body))\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"create TTS request: %w\", err)\n\t}\n\n\thttpReq.Header.Set(\"X-API-Key\", cfg.APIKey)\n\thttpReq.Header.Set(\"Cartesia-Version\", cartesiaAPIVersion)\n\thttpReq.Header.Set(\"Content-Type\", \"application/json\")\n\treturn httpReq, nil\n}\n\n// CloneVoice creates a cloned voice from an audio sample URL via Cartesia API.\nfunc CloneVoice(ctx context.Context, cfg VoiceConfig, name, audioURL string) (*VoiceCloneResponse, error) {\n\treq := VoiceCloneRequest{\n\t\tName:        name,\n\t\tDescription: fmt.Sprintf(\"Cloned voice for %s\", name),","sourceCodeStart":100,"sourceCodeEnd":136,"githubUrl":"https://github.com/Billionmail/BillionMail/blob/fc36c76c050c3775c5e899faf7403cf0262d2744/core/internal/service/video_gen/voice.go#L100-L136","documentation":"BuildTTSRequest marshals the TTSRequest struct to JSON before building the Cartesia text-to-speech HTTP request; a json.Marshal failure is wrapped as 'marshal TTS request: %w'. Like the clone-request counterpart, this is a defensive wrapper since marshaling a plain struct essentially never fails.","triggerScenarios":"json.Marshal(req) errors, only realistic if TTSRequest gains an unmarshalable field (chan/func/cyclic reference) or a field type with a broken custom MarshalJSON.","commonSituations":"Code changes that add unsupported field types to TTSRequest; a transcript modeled as a complex type with a faulty marshaller; ordinary runs with plain strings never reach this error.","solutions":["Review recent changes to TTSRequest for unsupported types or a broken custom MarshalJSON","Keep transcript as a plain string/simple struct so json.Marshal cannot fail","Extend TestBuildTTSRequest_Body/LongTranscript to cover any new fields after schema changes"],"exampleFix":"// before\ntype TTSRequest struct {\n    Transcript string          `json:\"transcript\"`\n    Meta       map[string]any  `json:\"meta\"`\n}\n// Meta containing a chan/func value at runtime makes json.Marshal fail\n// after\ntype TTSRequest struct {\n    Transcript string          `json:\"transcript\"`\n    Meta       map[string]string `json:\"meta\"` // JSON-safe value type\n}","handlingStrategy":"validation","validationCode":"func validateTTSRequest(req TTSRequest) error {\n    if req.Transcript == \"\" {\n        return fmt.Errorf(\"TTS transcript must not be empty\")\n    }\n    if _, err := json.Marshal(req); err != nil {\n        return fmt.Errorf(\"TTS request not serializable: %w\", err)\n    }\n    return nil\n}","typeGuard":null,"tryCatchPattern":"httpReq, err := video_gen.BuildTTSRequest(cfg, req)\nif err != nil {\n    var ume *json.UnsupportedTypeError\n    if errors.As(err, &ume) {\n        log.Errorf(\"unmarshalable field in TTSRequest: %v\", ume)\n    }\n    return err\n}","preventionTips":["Keep TTSRequest fields JSON-serializable; exclude internal fields with json:\"-\"","Run TestBuildTTSRequest_Body and LongTranscript after struct changes","Store transcript as a plain string","Avoid untyped map[string]any fields that can hold unmarshalable values"],"tags":["json","serialization","cartesia","tts"],"backgroundTag":"json-marshal-failed","analyzedSha":"fc36c76c050c3775c5e899faf7403cf0262d2744","analyzedAt":"2026-09-05T21:28:54.019Z","contentChangedAt":"2026-09-05T21:28:54.019Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}