{"record":{"id":"d928884545653913","repo":"zeroclaw-labs/zeroclaw","slug":"tts-returned-empty-audio-d92888","errorCode":null,"errorMessage":"TTS returned empty audio","messagePattern":"TTS returned empty audio","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/zeroclaw-channels/src/whatsapp_web.rs","lineNumber":1706,"sourceCode":"\n    /// Synthesize text to speech and send as a WhatsApp voice note (static version for spawned tasks).\n    #[cfg(feature = \"whatsapp-web\")]\n    async fn synthesize_voice_static(\n        client: &whatsapp_rust::Client,\n        to: &wacore_binary::jid::Jid,\n        text: &str,\n        tts_manager: &super::tts::TtsManager,\n    ) -> Result<()> {\n        let audio_bytes = tts_manager.synthesize_opus(text).await?;\n        let audio_len = audio_bytes.len();\n        ::zeroclaw_log::record!(\n            INFO,\n            ::zeroclaw_log::Event::new(module_path!(), ::zeroclaw_log::Action::Note),\n            &format!(\"TTS: synthesized {} bytes of audio\", audio_len)\n        );\n\n        if audio_bytes.is_empty() {\n            anyhow::bail!(\"TTS returned empty audio\");\n        }\n\n        use wacore::download::MediaType;\n        use whatsapp_rust::upload::UploadOptions;\n        let upload = client\n            .upload(audio_bytes, MediaType::Audio, UploadOptions::default())\n            .await\n            .map_err(|e| {\n                ::zeroclaw_log::record!(\n                    ERROR,\n                    ::zeroclaw_log::Event::new(module_path!(), ::zeroclaw_log::Action::Fail)\n                        .with_outcome(::zeroclaw_log::EventOutcome::Failure)\n                        .with_attrs(::serde_json::json!({\"error\": format!(\"{}\", e)})),\n                    \"Failed to upload TTS audio\"\n                );\n                anyhow::Error::msg(format!(\"Failed to upload TTS audio: {e}\"))\n            })?;\n","sourceCodeStart":1688,"sourceCodeEnd":1724,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-channels/src/whatsapp_web.rs#L1688-L1724","documentation":"Before uploading synthesized speech, the WhatsApp Web channel verifies the TTS step produced at least one byte and bails on an empty result. The immediately preceding log line (`TTS: synthesized N bytes of audio`) tells you whether the provider really returned zero bytes, distinguishing a provider problem from an upload problem.","triggerScenarios":"The TTS provider returns success with an empty body/zero bytes for the requested text — silent or whitespace-only input text, an unsupported voice/language combination, or a provider outage/quota error swallowed upstream.","commonSituations":"Empty message text routed into a voice flow; misconfigured TTS voice or language code; provider API change returning 200 with empty audio; quota exhausted mid-campaign.","solutions":["Check the preceding log line — `synthesized 0 bytes` confirms a provider-side issue rather than WhatsApp upload failure.","Skip TTS for empty/blank text and send plain text instead.","Verify the TTS provider configuration (voice, language, API key, quota) and test it directly with the same text."],"exampleFix":"// before: hand any text straight to the voice flow\nchannel.send(&SendMessage::new(voice_marker_for(&text), to)).await?;\n\n// after: only voice non-trivial text\nif text.trim().is_empty() {\n    return Ok(()); // nothing worth synthesizing\n}\nchannel.send(&SendMessage::new(voice_marker_for(&text), to)).await?;","handlingStrategy":"fallback","validationCode":"if text.trim().is_empty() {\n    // nothing to synthesize: skip the voice flow entirely\n    return Ok(());\n}\nchannel.send(&SendMessage::new(voice_message_for(&text), to)).await","typeGuard":"fn speakable_text(text: &str) -> bool {\n    !text.trim().is_empty()\n}","tryCatchPattern":"match send_voice(&channel, &text, to).await {\n    Err(e) if e.to_string().contains(\"TTS returned empty audio\") => {\n        // degrade to plain text instead of dropping the message\n        channel.send(&SendMessage::new(text, to)).await\n    }\n    other => other,\n}","preventionTips":["Skip voice conversion for blank/whitespace text before triggering the flow.","Monitor the `TTS: synthesized N bytes` log line; a stream of `0 bytes` means provider trouble, not WhatsApp trouble.","Smoke-test the TTS provider config (voice, language, quota) in CI."],"tags":["whatsapp-web","tts","audio","validation","rust"],"backgroundTag":"empty-response-body","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}