{"record":{"id":"8246a879ef44373e","repo":"Hmbown/CodeWhale","slug":"model-model-requires-cloned-voice-data-pass","errorCode":null,"errorMessage":"Model '{model}' requires cloned voice data. Pass --clone-voice <mp3|wav> or --voice <data-uri>.","messagePattern":"Model '(.+?)' requires cloned voice data\\. Pass --clone-voice <mp3\\|wav> or --voice <data-uri>\\.","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/tui/src/client.rs","lineNumber":3360,"sourceCode":"        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() {\n            anyhow::bail!(\n                \"Model '{model}' requires a voice design prompt. Pass --voice-prompt or --instruction.\"\n            );\n        }\n        if model_lower.contains(\"voiceclone\") && voice.is_none() {\n            anyhow::bail!(\n                \"Model '{model}' requires cloned voice data. Pass --clone-voice <mp3|wav> or --voice <data-uri>.\"\n            );\n        }\n\n        let mut audio = json!({\n            \"format\": audio_format.clone(),\n        });\n        if let Some(voice) = voice.as_deref() {\n            audio[\"voice\"] = json!(voice);\n        }\n\n        let body = build_speech_synthesis_body(&model, &text, instruction, audio);\n\n        let url = api_url(&self.base_url, \"chat/completions\");\n        let response = self.send_json_with_retry(&url, &body).await?;\n        let status = response.status();\n        if !status.is_success() {\n            let raw_error_text = bounded_error_text(response, ERROR_BODY_MAX_BYTES).await;","sourceCodeStart":3342,"sourceCodeEnd":3378,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/client.rs#L3342-L3378","documentation":"Speech synthesis via a *voiceclone* model requires reference audio of the voice to clone. The client validates this before sending any request: if the resolved model name contains 'voiceclone' and no voice payload (`request.voice`, e.g. a data URI) was supplied, it fails fast with this error instead of producing a confusing provider-side 4xx. It is a local precondition check in `crates/tui/src/client.rs` around line 3360.","triggerScenarios":"Calling speech synthesis with a model whose lowercased name contains \"voiceclone\" while `request.voice` is None, empty, or whitespace-only. This happens when the user omits `--clone-voice <mp3|wav>` / `--voice <data-uri>` on the CLI, or passes a path that failed to load into a data URI upstream.","commonSituations":"Developers switching a script from a standard TTS model to a clone model and forgetting the reference audio; passing a plain file path instead of a data URI so the voice field is treated as absent; environment configs where the clone audio is resolved conditionally and silently skipped.","solutions":["Pass `--clone-voice <mp3|wav>` so the CLI encodes the reference audio as a data URI into `voice`.","Or pass `--voice <data-uri>` with an explicit data: URI payload for the reference audio.","Verify the request struct's `voice` field is populated (non-empty after trim) before calling synthesis.","If cloning is not intended, switch to a non-voiceclone model."],"exampleFix":"// before\ncodewhale speak --model elevenlabs_voiceclone_v1 --text \"hello\"\n// after\ncodewhale speak --model elevenlabs_voiceclone_v1 --text \"hello\" --clone-voice ./my-voice.mp3","handlingStrategy":"validation","validationCode":"fn ensure_voice_present(model: &str, voice: Option<&str>) -> Result<(), String> {\n    if model.to_ascii_lowercase().contains(\"voiceclone\")\n        && voice.map(str::trim).filter(|v| !v.is_empty()).is_none()\n    {\n        return Err(format!(\"Model '{model}' requires --clone-voice <mp3|wav> or --voice <data-uri>.\"));\n    }\n    Ok(())\n}","typeGuard":"fn has_voice(voice: Option<&str>) -> bool {\n    voice.map(str::trim).filter(|v| !v.is_empty()).is_some()\n}","tryCatchPattern":null,"preventionTips":["Always pass --clone-voice when using a *voiceclone* model.","Encode reference audio as a data URI, not a bare file path.","Validate request.voice is non-empty before invoking synthesis."],"tags":["cli","audio","validation"],"backgroundTag":"missing-required-argument","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"}