{"record":{"id":"4ad2b9b37f0a6290","repo":"Hmbown/CodeWhale","slug":"speech-text-cannot-be-empty","errorCode":null,"errorMessage":"Speech text cannot be empty","messagePattern":"Speech text cannot be empty","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/tui/src/client.rs","lineNumber":3336,"sourceCode":"        &self,\n        request: SpeechSynthesisRequest,\n    ) -> Result<SpeechSynthesisResponse> {\n        let _inference = self.acquire_remote_control_inference_permit().await;\n        let _permit = self.acquire_provider_request_permit().await;\n        if self.api_provider != crate::config::ApiProvider::XiaomiMimo {\n            anyhow::bail!(\n                \"speech synthesis requires provider 'xiaomi-mimo' (current: {})\",\n                self.api_provider.as_str()\n            );\n        }\n\n        let model = request.model.trim().to_string();\n        if model.is_empty() {\n            anyhow::bail!(\"Speech model cannot be empty\");\n        }\n        let text = request.text.trim().to_string();\n        if text.is_empty() {\n            anyhow::bail!(\"Speech text cannot be empty\");\n        }\n\n        let audio_format = normalize_audio_format(&request.audio_format);\n        let model = self.wire_model_for_route(&model);\n        let model_lower = model.to_ascii_lowercase();\n        let instruction = request\n            .instruction\n            .as_deref()\n            .map(str::trim)\n            .filter(|value| !value.is_empty());\n        let voice = request\n            .voice\n            .as_deref()\n            .map(str::trim)\n            .filter(|value| !value.is_empty())\n            .map(str::to_string);\n\n        if model_lower.contains(\"voicedesign\") && instruction.is_none() {","sourceCodeStart":3318,"sourceCodeEnd":3354,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/client.rs#L3318-L3354","documentation":"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.","triggerScenarios":"Calling speech synthesis with text = \"\" or only whitespace (e.g. piping empty stdin into a TTS command).","commonSituations":"Shell pipeline producing no stdin data (echo \"\" | ... tts); clipboard read returning empty; upstream command failure silently feeding empty text.","solutions":["Supply non-empty text in SpeechSynthesisRequest.text.","If piping input, verify the upstream command actually produced output before invoking TTS.","Check clipboard/file sources are non-empty before passing them."],"exampleFix":"// before\nlet text = std::env::var(\"TTS_TEXT\")?; // may be empty\n\n// after\nlet text = std::env::var(\"TTS_TEXT\")?;\nif text.trim().is_empty() { anyhow::bail!(\"no text to synthesize\"); }","handlingStrategy":"validation","validationCode":"if request.text.trim().is_empty() {\n    return Err(anyhow::anyhow!(\"speech text cannot be empty\"));\n}","typeGuard":null,"tryCatchPattern":"match result {\n    Err(e) if e.to_string() == \"Speech text cannot be empty\" => {\n        eprintln!(\"No text supplied to synthesize; check your input source\");\n    }\n    r => r?,\n}","preventionTips":["Validate piped/clipboard input is non-empty before calling TTS.","Fail the upstream pipeline early when a source produces no text."],"tags":["speech","validation","empty","request"],"backgroundTag":"empty-required-field","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}