zeroclaw-labs/zeroclaw · error

Telegram sendVoice failed: {err}

Error message

Telegram sendVoice failed: {err}

What it means

sendVoice() uploads a multipart sendVoice request and bails with Telegram's response body on non-2xx. Telegram only accepts voice notes as OGG encoded with Opus, so this error fires when that contract or the chat itself is broken.

Source

Thrown at crates/zeroclaw-channels/src/telegram.rs:3462

        if let Some(tid) = thread_id {
            form = form.text("message_thread_id", tid.to_string());
        }

        if let Some(cap) = caption {
            form = form.text("caption", cap.to_string());
        }

        let resp = self
            .http_client()
            .post(self.api_url("sendVoice"))
            .multipart(form)
            .send()
            .await?;

        if !resp.status().is_success() {
            let err = resp.text().await?;
            anyhow::bail!("Telegram sendVoice failed: {err}");
        }

        ::zeroclaw_log::record!(
            INFO,
            ::zeroclaw_log::Event::new(module_path!(), ::zeroclaw_log::Action::Note)
                .with_attrs(::serde_json::json!({"chat_id": chat_id, "file_name": file_name})),
            "voice sent to"
        );
        Ok(())
    }

    /// Send a file by URL (Telegram will download it)
    pub async fn send_document_by_url(
        &self,
        chat_id: &str,
        thread_id: Option<&str>,
        url: &str,
        caption: Option<&str>,

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Read the embedded `description` for the exact reason.
  2. Verify the file is OGG/Opus: `ffprobe file.ogg` should report codec=opus; re-encode with `ffmpeg -i in.wav -c:a libopus out.ogg`.
  3. Confirm chat_id/thread_id are valid and the bot is not blocked.
  4. On 429, honor retry_after and resend once.
Defensive patterns

Strategy: validation

Validate before calling

// confirm the payload is OGG/Opus before uploading
let head = tokio::fs::read(&path).await?;
anyhow::ensure!(head.starts_with(b"OggS"), "voice file is not an OGG container");

Type guard

fn is_ogg_opus(path: &Path) -> bool {
    // cheap header check; ffprobe -c:a opus is authoritative
    std::fs::read(path).map(|b| b.starts_with(b"OggS")).unwrap_or(false)
}

Try / catch

if let Err(e) = channel.send_voice(chat, thread, path, None).await {
    if e.to_string().contains("wrong type of the current file") {
        let fixed = transcode_to_opus(path).await?; // ffmpeg -c:a libopus
        return channel.send_voice(chat, thread, &fixed, None).await;
    }
    return Err(e);
}

Prevention

When it happens

Trigger: 400 'wrong type of the current file' when the upload is OGG/Vorbis, WAV, or MP3 instead of OGG/Opus; 400 chat not found / invalid thread_id; 403 blocked/kicked; 429 flood control; oversized voice buffers.

Common situations: TTS pipeline skipping the ffmpeg Opus transcode after a config change; ffmpeg writing default vorbis codec; speech clips converted with the wrong flags; rate limits during rapid conversational replies.

Related errors


AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23). Data as JSON: /api/errors/d165ca5a41c27675. Report an issue: GitHub.