{"record":{"id":"64b50651c93342ce","repo":"Hmbown/CodeWhale","slug":"speech-model-cannot-be-empty","errorCode":null,"errorMessage":"Speech model cannot be empty","messagePattern":"Speech model cannot be empty","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/tui/src/client.rs","lineNumber":3332,"sourceCode":"    /// MiMo's TTS chat-completions surface expects that shape. The optional\n    /// `instruction` is a `user` message that controls style, voice design, or\n    /// voice-clone performance and is not spoken verbatim.\n    pub async fn synthesize_speech(\n        &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)","sourceCodeStart":3314,"sourceCodeEnd":3350,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/client.rs#L3314-L3350","documentation":"synthesize_speech preconditions: the request named an empty speech model. Speech synthesis on the Xiaomi MiMo TTS surface requires an exact non-empty TTS model id, so a blank model is rejected before any provider call.","triggerScenarios":"Calling speech synthesis with `SpeechSynthesisRequest { model: \"\" }` or model consisting only of whitespace.","commonSituations":"A CLI/default where the speech model config key was never set; a slash command invoked without the model argument; config parse yielding an empty string.","solutions":["Set the speech model in the request (e.g. the xiaomi voice model you intend to use).","Set the speech model in config.toml / the relevant config key so defaults populate it.","If invoking via CLI/command, pass the model argument explicitly."],"exampleFix":"// before\nsynthesize(SpeechSynthesisRequest { model: \"\".into(), .. }).await?;\n\n// after\nsynthesize(SpeechSynthesisRequest { model: \"milivo-speech-02-turbo\".into(), .. }).await?;","handlingStrategy":"validation","validationCode":"fn validate_speech_request(req: &SpeechSynthesisRequest) -> Result<(), String> {\n    if req.model.trim().is_empty() { return Err(\"model must be set\".into()); }\n    if req.text.trim().is_empty() { return Err(\"text must be set\".into()); }\n    Ok(())\n}","typeGuard":null,"tryCatchPattern":"match result {\n    Err(e) if e.to_string() == \"Speech model cannot be empty\" => {\n        eprintln!(\"Set a speech model via config or --model flag\");\n    }\n    r => r?,\n}","preventionTips":["Set a default speech model in config.toml.","Reject empty model/text in the CLI layer before building the request."],"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"}