sipeed/picoclaw · error

invalid TTS response: missing audio data

Error message

invalid TTS response: missing audio data

What it means

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.

Source

Thrown at pkg/audio/tts/mimo_tts.go:153

	}

	var payload struct {
		Choices []struct {
			Message struct {
				Audio struct {
					Data string `json:"data"`
				} `json:"audio"`
			} `json:"message"`
		} `json:"choices"`
	}

	err = json.Unmarshal(body, &payload)
	if err != nil {
		return nil, fmt.Errorf("failed to decode response: %w", err)
	}

	if len(payload.Choices) == 0 || payload.Choices[0].Message.Audio.Data == "" {
		return nil, fmt.Errorf("invalid TTS response: missing audio data")
	}

	audioBytes, err := base64.StdEncoding.DecodeString(payload.Choices[0].Message.Audio.Data)
	if err != nil {
		return nil, fmt.Errorf("failed to decode audio data: %w", err)
	}

	return io.NopCloser(bytes.NewReader(audioBytes)), nil
}

View on GitHub (pinned to 49183d7e8d)

Solutions

  1. Set the model explicitly to mimo-v2-tts
  2. Keep the default voice default_zh unless the provider documents alternatives
  3. Smoke-test a short plain 'hello' synthesis; if that fails it is model/voice config, not the input
  4. Fall back to the OpenAI TTS provider — SynthesizeAndStore accepts any TTSProvider

Example fix

// before
p := tts.NewMimoTTSProvider(key, base, `mimo-v2`, ``) // text model: no audio
// after
p := tts.NewMimoTTSProvider(key, base, `mimo-v2-tts`, ``)
Defensive patterns

Strategy: fallback

Type guard

func isMissingAudio(err error) bool {
    return err != nil && strings.Contains(err.Error(), `missing audio data`)
}

Try / catch

if isMissingAudio(err) && openaiProvider != nil {
    // mimo answered without audio (model/voice config): fall back to another TTSProvider
    stream, err = openaiProvider.Synthesize(ctx, text)
}

Prevention

When it happens

Trigger: 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.

Common situations: Guessing model names in config; provider changing default voice aliases (noted in the code comment); inputs with characters the TTS refuses.

Related errors


AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15). Data as JSON: /api/errors/0bc79d683d0868f5. Report an issue: GitHub.