Hmbown/CodeWhale · error

Speech model cannot be empty

Error message

Speech model cannot be empty

What it means

Input validation in `synthesize_speech`: after the provider gate, the requested model string is trimmed and must be non-empty before the request can be built. This fires before text validation and before any network I/O — purely a caller-input check on `SpeechSynthesisRequest.model`.

Source

Thrown at crates/tui/src/client.rs:2345

    ///
    /// The spoken text is placed in an `assistant` message because Xiaomi
    /// MiMo's TTS chat-completions surface expects that shape. The optional
    /// `instruction` is a `user` message that controls style, voice design, or
    /// voice-clone performance and is not spoken verbatim.
    pub async fn synthesize_speech(
        &self,
        request: SpeechSynthesisRequest,
    ) -> Result<SpeechSynthesisResponse> {
        if self.api_provider != crate::config::ApiProvider::XiaomiMimo {
            anyhow::bail!(
                "speech synthesis requires provider 'xiaomi-mimo' (current: {})",
                self.api_provider.as_str()
            );
        }

        let model = request.model.trim().to_string();
        if model.is_empty() {
            anyhow::bail!("Speech model cannot be empty");
        }
        let text = request.text.trim().to_string();
        if text.is_empty() {
            anyhow::bail!("Speech text cannot be empty");
        }

        let audio_format = normalize_audio_format(&request.audio_format);
        let model = wire_model_for_provider_route(self.api_provider, &self.base_url, &model);
        let model_lower = model.to_ascii_lowercase();
        let instruction = request
            .instruction
            .as_deref()
            .map(str::trim)
            .filter(|value| !value.is_empty());
        let voice = request
            .voice
            .as_deref()
            .map(str::trim)

View on GitHub (pinned to 8880682c63)

Solutions

  1. Pass an explicit TTS model id, e.g. `--model xiaomi-mimo-tts` (or the specific MiMo voice model you intend).
  2. If embedding, default the model in the caller before constructing the request: `let model = request.model.trim(); if model.is_empty() { /* pick default */ }`.
  3. Check for unset shell variables when scripting (`--model "${TTS_MODEL:?TTS_MODEL not set}"`).

Example fix

# before
/speech --model "" "hello"
# after
/speech --model xiaomi-mimo-tts "hello"
Defensive patterns

Strategy: validation

Validate before calling

fn speech_request_valid(model: &str, text: &str) -> bool {
    !model.trim().is_empty() && !text.trim().is_empty()
}

Type guard

fn non_blank(s: &str) -> Option<&str> {
    let t = s.trim();
    (!t.is_empty()).then_some(t)
}

Prevention

When it happens

Trigger: Invoking the speech command with no `--model` argument (or a whitespace-only value) when no configured default supplies one; programmatically building a `SpeechSynthesisRequest` with `model: ""` or `" "`.

Common situations: Command-line invocation that assumes a default TTS model exists; UI state where the model selector was left blank; scripts passing an empty variable (`--model "$TTS_MODEL"` with the env var unset).

Related errors


AI-assisted analysis of Hmbown/CodeWhale@8880682c63 (2026-08-16). Data as JSON: /api/errors/64b50651c93342ce. Report an issue: GitHub.