zeroclaw-labs/zeroclaw · error
Telegram sendDocument by URL failed: {err}
Error message
Telegram sendDocument by URL failed: {err} What it means
The URL variant of document sending posts a JSON body (sendDocument with a URL) and bails with Telegram's response body on non-2xx. Telegram fetches the URL server-side, so failures usually mean the URL was unreachable, the wrong content type, or the chat was undeliverable.
Source
Thrown at crates/zeroclaw-channels/src/telegram.rs:3504
if let Some(tid) = thread_id {
body["message_thread_id"] = serde_json::Value::String(tid.to_string());
}
if let Some(cap) = caption {
body["caption"] = serde_json::Value::String(cap.to_string());
}
let resp = self
.http_client()
.post(self.api_url("sendDocument"))
.json(&body)
.send()
.await?;
if !resp.status().is_success() {
let err = resp.text().await?;
anyhow::bail!("Telegram sendDocument by URL 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, "url": url})),
"document (URL) sent to"
);
Ok(())
}
/// Send a photo by URL (Telegram will download it)
pub async fn send_photo_by_url(
&self,
chat_id: &str,
thread_id: Option<&str>,
url: &str,
caption: Option<&str>,View on GitHub (pinned to 88bb9c8533)
Solutions
- curl the URL from a public network — Telegram must be able to fetch exactly that file.
- Use the direct file link with a document-appropriate Content-Type, not an HTML page.
- For private hosts, download locally and use send_document (multipart) instead.
- Read the embedded `description` for the specific Bot API reason.
Defensive patterns
Strategy: fallback
Validate before calling
let resp = reqwest::Client::new().head(url).send().await?;
anyhow::ensure!(resp.status().is_success(), "document URL not fetchable: {}", resp.status());
anyhow::ensure!(!url.starts_with("http://localhost") && !url.starts_with("http://127."), "URL must be reachable by Telegram"); Type guard
fn is_url_content_failure(err: &anyhow::Error) -> bool {
err.to_string().contains("failed to get HTTP URL content")
} Try / catch
if let Err(e) = channel.send_document_by_url(chat, url).await {
if is_url_content_failure(&e) {
let bytes = reqwest::get(url).await?.bytes().await?;
return channel.send_document_bytes(chat, thread, bytes.to_vec(), name, None).await;
}
return Err(e);
} Prevention
- Use direct, public, non-expiring file URLs for sendDocument-by-URL.
- For private hosts, download locally and use the multipart path.
- Pre-check with a HEAD request including expected content-type.
When it happens
Trigger: 400 'failed to get HTTP URL content' when Telegram cannot download the URL (private host, expired signed link, hotlink protection, HTML redirect instead of the file); malformed URL; 403 chat not found/blocked; document behind the URL over 20MB fetched by Telegram.
Common situations: Links to intranet/localhost artifact servers; presigned S3 URLs that expired before the send; CDN serving an HTML error page with 200; sending page links instead of direct file links.
Related errors
- {method} by URL failed: {err}
- Telegram sendPhoto by URL failed: {err}
- {method} failed: status={status}, body={body}
- Telegram file download failed: {}
- Telegram sendDocument failed: {err}
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/6e367feb92637c98.
Report an issue: GitHub.