{"record":{"id":"edb38a8f28dd8a45","repo":"chenhg5/cc-connect","slug":"minimax-tts-read-sse-stream-w","errorCode":null,"errorMessage":"minimax tts: read SSE stream: %w","messagePattern":"minimax tts: read SSE stream: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/tts.go","lineNumber":402,"sourceCode":"\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,\n// which is shaped like OpenAI chat completions: the synthesis text rides\n// on an assistant message and audio bytes come back base64-encoded inside\n// choices[0].message.audio.data.\n//\n// Docs: https://platform.xiaomimimo.com/#/docs/usage-guide/speech-synthesis\ntype MimoTTS struct {","sourceCodeStart":384,"sourceCodeEnd":420,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/core/tts.go#L384-L420","documentation":"This wraps the terminal error from bufio.Scanner while reading the MiniMax SSE stream (scanner.Err()). It means the response body could not be fully read — the connection broke or an I/O error occurred mid-stream, before the status=2 trailer chunk arrived. Any audio decoded so far is discarded.","triggerScenarios":"Connection reset or timeout while streaming chunks; server closed the connection prematurely; resp.Body read fails due to network interruption; scanner buffer still exceeded (though it is set to 10MB, very large lines could still error).","commonSituations":"Flaky Wi-Fi/mobile network during long TTS generation; LB/gateway idle-timeout killing slow streams; VPN drop mid-request.","solutions":["Retry the Synthesize call — mid-stream disconnects are usually transient.","Increase the HTTP client's timeout so long TTS streams are not cut off.","Check for a gateway/proxy with a low idle/read timeout and raise it or stream directly.","If persistent, capture the wrapped error to see if it is connection reset, unexpected EOF, or context deadline and address that root cause."],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"for attempt := 0; attempt < 3; attempt++ {\n    audio, format, err := tts.Synthesize(ctx, text, voice)\n    if err == nil { break }\n    if strings.Contains(err.Error(), \"read SSE stream\") {\n        time.Sleep(backoff(attempt)); continue\n    }\n    return err\n}","preventionTips":["Raise gateway/LB idle timeouts for long SSE streams","Use stable networks or retry on mobile/VPN connections","Set client timeouts well above expected synthesis duration"],"tags":["go","network","streaming","sse","tts","minimax"],"backgroundTag":"network-request-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"}