{"record":{"id":"c85cdbbdda5a7f06","repo":"Hmbown/CodeWhale","slug":"speech-requires-provider-xiaomi-mimo-curren","errorCode":null,"errorMessage":"`speech` requires provider = \"xiaomi-mimo\" (current: {}). Run with `--provider xiaomi-mimo` or set it in config.","messagePattern":"`speech` requires provider = \"xiaomi-mimo\" \\(current: (.+?)\\)\\. Run with `--provider xiaomi-mimo` or set it in config\\.","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/tui/src/lib.rs","lineNumber":7355,"sourceCode":"        default_speech_output_name, describe_speech_voice, encode_voice_clone_sample_data_uri,\n        infer_speech_model, normalize_speech_format,\n    };\n\n    let SpeechArgs {\n        text,\n        output,\n        output_dir,\n        model,\n        voice,\n        instruction,\n        voice_prompt,\n        clone_voice,\n        format,\n        json: json_output,\n    } = args;\n\n    if config.api_provider() != ApiProvider::XiaomiMimo {\n        bail!(\n            \"`speech` requires provider = \\\"xiaomi-mimo\\\" (current: {}). Run with `--provider xiaomi-mimo` or set it in config.\",\n            config.api_provider().as_str()\n        );\n    }\n\n    if text.trim().is_empty() {\n        bail!(\"Speech text cannot be empty\");\n    }\n    let voice_is_data_uri = voice\n        .as_deref()\n        .map(str::trim)\n        .is_some_and(|value| value.starts_with(\"data:audio/\"));\n    if clone_voice.is_some() && voice.is_some() {\n        bail!(\"Use either --clone-voice or --voice for cloned voice data, not both\");\n    }\n    let model = infer_speech_model(\n        model.as_deref(),\n        clone_voice.is_some() || voice_is_data_uri,","sourceCodeStart":7337,"sourceCodeEnd":7373,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/0c42157ee52f9d55af2b506d71b46249910f77d3/crates/tui/src/lib.rs#L7337-L7373","documentation":"The `speech` subcommand is wired only to the Xiaomi MiMo TTS API. Before building any request it asserts config.api_provider() == ApiProvider::XiaomiMimo and bails with the current provider's name plus the two remediation paths: the `--provider xiaomi-mimo` flag or a config change. No network I/O happens before this check.","triggerScenarios":"Running `codewhale speech ...` while the active config's api_provider resolves to anything other than xiaomi-mimo (e.g. a chat-provider default). The check is the first thing after argument destructuring.","commonSituations":"A config.toml defaulting to the chat provider for coding work; switching providers globally and forgetting speech is MiMo-only; a fresh machine where the default provider was never changed.","solutions":["Add `--provider xiaomi-mimo` to the speech invocation","Or set provider = \"xiaomi-mimo\" in the config that speech runs under","Confirm the active provider afterwards with `codewhale doctor`"],"exampleFix":"// before\n$ codewhale speech ...\nError: `speech` requires provider = \"xiaomi-mimo\" (current: deepseek). Run with `--provider xiaomi-mimo` or set it in config.\n\n// after\n$ codewhale speech --provider xiaomi-mimo ...","handlingStrategy":"validation","validationCode":"# pin the provider explicitly so config drift cannot break speech jobs\nexec codewhale speech --provider xiaomi-mimo \"$@\"","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always pass --provider explicitly for speech in scripts and aliases","Document that speech is MiMo-only next to any provider-switching runbook"],"tags":["speech","tts","provider","cli"],"backgroundTag":"provider-mismatch","analyzedSha":"0c42157ee52f9d55af2b506d71b46249910f77d3","analyzedAt":"2026-08-20T21:50:45.477Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}