{"record":{"id":"0bc79d683d0868f5","repo":"sipeed/picoclaw","slug":"invalid-tts-response-missing-audio-data","errorCode":null,"errorMessage":"invalid TTS response: missing audio data","messagePattern":"invalid TTS response: missing audio data","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/audio/tts/mimo_tts.go","lineNumber":153,"sourceCode":"\t}\n\n\tvar payload struct {\n\t\tChoices []struct {\n\t\t\tMessage struct {\n\t\t\t\tAudio struct {\n\t\t\t\t\tData string `json:\"data\"`\n\t\t\t\t} `json:\"audio\"`\n\t\t\t} `json:\"message\"`\n\t\t} `json:\"choices\"`\n\t}\n\n\terr = json.Unmarshal(body, &payload)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to decode response: %w\", err)\n\t}\n\n\tif len(payload.Choices) == 0 || payload.Choices[0].Message.Audio.Data == \"\" {\n\t\treturn nil, fmt.Errorf(\"invalid TTS response: missing audio data\")\n\t}\n\n\taudioBytes, err := base64.StdEncoding.DecodeString(payload.Choices[0].Message.Audio.Data)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to decode audio data: %w\", err)\n\t}\n\n\treturn io.NopCloser(bytes.NewReader(audioBytes)), nil\n}\n","sourceCodeStart":135,"sourceCodeEnd":163,"githubUrl":"https://github.com/sipeed/picoclaw/blob/49183d7e8daed0dba89ddbb6fcb60089401d9680/pkg/audio/tts/mimo_tts.go#L135-L163","documentation":"Returned when the MiMo endpoint returns HTTP 200 with valid JSON but choices is empty or choices[0].message.audio.data is empty. The model answered as chat text instead of synthesizing audio — typically a non-TTS model name, a voice the endpoint silently rejects, or moderation refusing to vocalize the input.","triggerScenarios":"Model set to a text-only model (e.g. 'mimo-v2') instead of 'mimo-v2-tts'; a voice value the endpoint does not map to audio (the code pins default_zh because mimo_default resolves to default_en); content moderation on the input text.","commonSituations":"Guessing model names in config; provider changing default voice aliases (noted in the code comment); inputs with characters the TTS refuses.","solutions":["Set the model explicitly to mimo-v2-tts","Keep the default voice default_zh unless the provider documents alternatives","Smoke-test a short plain 'hello' synthesis; if that fails it is model/voice config, not the input","Fall back to the OpenAI TTS provider — SynthesizeAndStore accepts any TTSProvider"],"exampleFix":"// before\np := tts.NewMimoTTSProvider(key, base, `mimo-v2`, ``) // text model: no audio\n// after\np := tts.NewMimoTTSProvider(key, base, `mimo-v2-tts`, ``)","handlingStrategy":"fallback","validationCode":null,"typeGuard":"func isMissingAudio(err error) bool {\n    return err != nil && strings.Contains(err.Error(), `missing audio data`)\n}","tryCatchPattern":"if isMissingAudio(err) && openaiProvider != nil {\n    // mimo answered without audio (model/voice config): fall back to another TTSProvider\n    stream, err = openaiProvider.Synthesize(ctx, text)\n}","preventionTips":["Use mimo-v2-tts, never the text model, for synthesis","Keep voice default_zh unless the provider documents alternatives","Smoke-test a short 'hello' synthesis after every model config change"],"tags":["api","tts","mimo","configuration"],"backgroundTag":null,"analyzedSha":"49183d7e8daed0dba89ddbb6fcb60089401d9680","analyzedAt":"2026-08-15T21:55:41.315Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}