Hmbown/CodeWhale · error · anyhow::Error

Speech text cannot be empty

Error message

Speech text cannot be empty

What it means

Input guard on the speech-synthesis path in client.rs: after the provider check (xiaomi-mimo only) and model check, the trimmed request text must be non-empty. A blank text string is rejected before any HTTP request is made.

Solutions

  1. Supply non-empty text in SpeechSynthesisRequest.text.
  2. If piping input, verify the upstream command actually produced output before invoking TTS.
  3. Check clipboard/file sources are non-empty before passing them.

Example fix

// before
let text = std::env::var("TTS_TEXT")?; // may be empty

// after
let text = std::env::var("TTS_TEXT")?;
if text.trim().is_empty() { anyhow::bail!("no text to synthesize"); }
Defensive patterns

Strategy: validation

Validate before calling

if request.text.trim().is_empty() {
    return Err(anyhow::anyhow!("speech text cannot be empty"));
}

Try / catch

match result {
    Err(e) if e.to_string() == "Speech text cannot be empty" => {
        eprintln!("No text supplied to synthesize; check your input source");
    }
    r => r?,
}

Prevention

When it happens

Trigger: Calling speech synthesis with text = "" or only whitespace (e.g. piping empty stdin into a TTS command).

Common situations: Shell pipeline producing no stdin data (echo "" | ... tts); clipboard read returning empty; upstream command failure silently feeding empty text.

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/4ad2b9b37f0a6290. Report an issue: GitHub.

Appendix: source

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

        &self,
        request: SpeechSynthesisRequest,
    ) -> Result<SpeechSynthesisResponse> {
        let _inference = self.acquire_remote_control_inference_permit().await;
        let _permit = self.acquire_provider_request_permit().await;
        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 = self.wire_model_for_route(&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)
            .filter(|value| !value.is_empty())
            .map(str::to_string);

        if model_lower.contains("voicedesign") && instruction.is_none() {

View on GitHub (pinned to 73e0f67d83)