{"record":{"id":"b4e72df172089895","repo":"zeroclaw-labs/zeroclaw","slug":"telegram-senddocument-failed-err","errorCode":null,"errorMessage":"Telegram sendDocument failed: {err}","messagePattern":"Telegram sendDocument failed: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/zeroclaw-channels/src/telegram.rs","lineNumber":3178,"sourceCode":"\n        if let Some(tid) = thread_id {\n            form = form.text(\"message_thread_id\", tid.to_string());\n        }\n\n        if let Some(cap) = caption {\n            form = form.text(\"caption\", cap.to_string());\n        }\n\n        let resp = self\n            .http_client()\n            .post(self.api_url(\"sendDocument\"))\n            .multipart(form)\n            .send()\n            .await?;\n\n        if !resp.status().is_success() {\n            let err = resp.text().await?;\n            anyhow::bail!(\"Telegram sendDocument failed: {err}\");\n        }\n\n        ::zeroclaw_log::record!(\n            INFO,\n            ::zeroclaw_log::Event::new(module_path!(), ::zeroclaw_log::Action::Note)\n                .with_attrs(::serde_json::json!({\"chat_id\": chat_id, \"file_name\": file_name})),\n            \"document sent to\"\n        );\n        Ok(())\n    }\n\n    /// Send a document from bytes (in-memory) to a Telegram chat\n    pub async fn send_document_bytes(\n        &self,\n        chat_id: &str,\n        thread_id: Option<&str>,\n        file_bytes: Vec<u8>,\n        file_name: &str,","sourceCodeStart":3160,"sourceCodeEnd":3196,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-channels/src/telegram.rs#L3160-L3196","documentation":"send_document() reads the file from disk and uploads it as a multipart sendDocument request (with optional caption and message_thread_id); any non-2xx answer bails with Telegram's response body embedded. The body's `description` is the authoritative reason.","triggerScenarios":"413 when the file exceeds Telegram's 50MB document limit; 400 'chat not found' for a bad chat_id or a thread_id that is not a forum topic; 403 blocked/kicked; 429 flood control; invalid multipart payload when the file disappears between the exists-check and tokio::fs::read.","commonSituations":"Exporting large logs/archives over 50MB; sending to a chat the bot was removed from; wrong thread_id for non-forum chats; rapid-fire document sends tripping limits.","solutions":["Read the embedded body — `description` names the exact cause.","Check the file size against the 50MB document cap and split or compress oversized files.","Verify chat_id and, when used, that thread_id refers to an actual forum topic in that chat.","On 429, honor retry_after and resend once."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"const DOC_LIMIT: u64 = 50 * 1024 * 1024;\nlet meta = tokio::fs::metadata(file_path).await?;\nanyhow::ensure!(meta.len() <= DOC_LIMIT, \"document {}MB exceeds 50MB\", meta.len() / 1024 / 1024);\nanyhow::ensure!(file_path.is_file(), \"attachment is not a regular file\");","typeGuard":"fn is_too_large(err: &anyhow::Error) -> bool {\n    err.to_string().contains(\"413\") || err.to_string().contains(\"file is too big\")\n}","tryCatchPattern":"if let Err(e) = channel.send_document(chat, thread, path, caption).await {\n    if is_too_large(&e) {\n        return channel.send_text_chunks(\"file too large to send\", chat, thread).await;\n    }\n    return Err(e);\n}","preventionTips":["Check file size against 50MB before reading it into memory.","Verify chat_id and thread_id (forum topics only) before posting.","Honor retry_after on 429; stop sending to 403 chats."],"tags":["telegram","senddocument","multipart","upload","size-limit"],"backgroundTag":"telegram-file-upload-failed","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}