{"record":{"id":"3ed1f17bd1dd20bd","repo":"chenhg5/cc-connect","slug":"minimax-tts-decode-audio-hex-w","errorCode":null,"errorMessage":"minimax tts: decode audio hex: %w","messagePattern":"minimax tts: decode audio hex: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/tts.go","lineNumber":396,"sourceCode":"\t\t\t} `json:\"base_resp\"`\n\t\t}\n\t\tif err := json.Unmarshal([]byte(data), &chunk); err != nil {\n\t\t\tcontinue\n\t\t}\n\t\tif chunk.BaseResp.StatusCode != 0 {\n\t\t\treturn nil, \"\", fmt.Errorf(\"minimax tts API error %d: %s\", chunk.BaseResp.StatusCode, chunk.BaseResp.StatusMsg)\n\t\t}\n\t\t// MiniMax T2A v2 stream protocol: status=1 carries incremental audio\n\t\t// chunks; the final status=2 chunk re-sends the full audio as a\n\t\t// trailer for non-stream clients. Appending the trailer doubles the\n\t\t// audio length and makes the spoken text play twice, so skip it.\n\t\tif chunk.Data.Status == 2 {\n\t\t\tbreak\n\t\t}\n\t\tif chunk.Data.Audio != \"\" {\n\t\t\taudioBytes, err := hex.DecodeString(chunk.Data.Audio)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, \"\", fmt.Errorf(\"minimax tts: decode audio hex: %w\", err)\n\t\t\t}\n\t\t\taudioBuf.Write(audioBytes)\n\t\t}\n\t}\n\tif err := scanner.Err(); err != nil {\n\t\treturn nil, \"\", fmt.Errorf(\"minimax tts: read SSE stream: %w\", err)\n\t}\n\tif audioBuf.Len() == 0 {\n\t\treturn nil, \"\", fmt.Errorf(\"minimax tts: no audio data received\")\n\t}\n\treturn audioBuf.Bytes(), \"mp3\", nil\n}\n\n// ──────────────────────────────────────────────────────────────\n// MimoTTS — Xiaomi MiMo-V2.5-TTS implementation\n// ──────────────────────────────────────────────────────────────\n\n// MimoTTS implements TextToSpeech using the Xiaomi MiMo-V2.5-TTS API,","sourceCodeStart":378,"sourceCodeEnd":414,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/core/tts.go#L378-L414","documentation":"MiniMax encodes each TTS audio chunk in the SSE stream as a hex string in data.audio; this error is returned when hex.DecodeString fails on that chunk. A decode failure means the stream payload was not valid hex — typically a truncated/corrupted stream, a proxy mangling the body, or a non-hex error payload reaching the audio branch.","triggerScenarios":"A data chunk's audio field contains non-hex characters (odd length, whitespace, HTML injected by an intermediary proxy); corrupted/partial SSE data from an unstable connection.","commonSituations":"Corporate proxy or captive portal rewriting response bodies; connection truncation mid-stream combined with parser quirks; MiniMax changing the wire format in a new API version.","solutions":["Check for an intercepting proxy/captive portal rewriting HTTPS bodies; bypass it or fix the proxy.","Retry the synthesis — a single corrupted chunk is usually transient.","Verify the configured base_url/API version still speaks the T2A v2 hex-chunk protocol after MiniMax updates.","Report/inspect the raw chunk if reproducible: dump the SSE body to confirm which characters broke hex decoding."],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":"func isHex(s string) bool { _, err := hex.DecodeString(s); return err == nil }","tryCatchPattern":"if err != nil && strings.Contains(err.Error(), \"decode audio hex\") {\n    // corrupted chunk: retry once; if persistent, log raw stream for provider support\n}","preventionTips":["Avoid intermediaries (proxies/antivirus) that rewrite HTTPS response bodies","Retry transient stream corruption automatically","Pin/verify the MiniMax API version the client targets","Log failing chunk samples to detect wire-format changes early"],"tags":["go","encoding","hex","tts","minimax","sse"],"backgroundTag":"unexpected-api-response-shape","analyzedSha":"4000b2338aa6e850c99df54f8b0ed6ed7460b401","analyzedAt":"2026-09-06T11:45:09.575Z","contentChangedAt":"2026-09-06T11:45:09.575Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}