{"record":{"id":"7d9ce2d3aa641267","repo":"Hmbown/CodeWhale","slug":"speech-synthesis-failed-http-status-error-tex","errorCode":null,"errorMessage":"Speech synthesis failed: HTTP {status}: {error_text}","messagePattern":"Speech synthesis failed: HTTP (.+?): (.+?)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/client.rs","lineNumber":2397,"sourceCode":"            \"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;\n            let error_text = sanitize_http_error_body(\n                Some(self.api_provider.display_name()),\n                status.as_u16(),\n                &raw_error_text,\n            );\n            anyhow::bail!(\"Speech synthesis failed: HTTP {status}: {error_text}\");\n        }\n\n        let response_text = response\n            .text()\n            .await\n            .context(\"Failed to read speech synthesis response body\")?;\n        let payload: Value = serde_json::from_str(&response_text)\n            .context(\"Failed to parse speech synthesis response JSON\")?;\n        let (audio_bytes, transcript) = parse_speech_audio_response(&payload)?;\n\n        Ok(SpeechSynthesisResponse {\n            model,\n            audio_format,\n            audio_bytes,\n            transcript,\n            voice,\n        })\n    }","sourceCodeStart":2379,"sourceCodeEnd":2415,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/8880682c63083a91624de936797efa3ce9e498fd/crates/tui/src/client.rs#L2379-L2415","documentation":"The speech-synthesis call posts to `{base_url}/chat/completions` (Xiaomi MiMo TTS shape) and bails when the HTTP status is non-2xx. The error body is read with a 64KB cap and passed through `sanitize_http_error_body` with the provider display name, so leaked keys in the body are redacted before the message is shown. All earlier validations (provider, model, text, voice requirements) have already passed when this fires.","triggerScenarios":"401/403 bad or expired Xiaomi MiMo credentials; 400 invalid `audio.format` value or malformed voice data-URI; 429 quota/rate exhaustion on the MiMo endpoint; 5xx upstream failures; wrong base_url routing TTS traffic to a non-MiMo gateway.","commonSituations":"Expired API key for the xiaomi-mimo provider; audio format string not in the accepted set (check `normalize_audio_format` accepted values); oversized clone-voice payload rejected upstream; regional endpoint quotas.","solutions":["Read the embedded provider error text — it is the sanitized upstream body and names the actual cause (auth, quota, invalid parameter).","401/403 → refresh the xiaomi-mimo credential in config and re-run.","400 → verify the audio format string and that the voice data-URI/clone file is valid and not truncated.","429/5xx → wait and retry; if persistent, check the provider status page and your quota."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"match client.synthesize_speech(req).await {\n    Ok(audio) => play(audio),\n    Err(e) => {\n        let msg = e.to_string();\n        if msg.contains(\"HTTP 401\") || msg.contains(\"HTTP 403\") { refresh_credentials(); }\n        else if msg.contains(\"HTTP 429\") { schedule_retry(backoff()); }\n        else { show_sanitized_error(msg); }\n    }\n}","preventionTips":["Run `codewhale doctor` after adding xiaomi-mimo credentials to catch auth issues before a long speech session.","Cache synthesized audio for repeated phrases to avoid quota burn.","Normalize/validate the audio format string and voice data-URI in the caller before sending."],"tags":["speech","tts","http","api","xiaomi-mimo"],"backgroundTag":null,"analyzedSha":"8880682c63083a91624de936797efa3ce9e498fd","analyzedAt":"2026-08-16T11:31:27.956Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}