{"record":{"id":"4a2ea2ff919d86f3","repo":"zeroclaw-labs/zeroclaw","slug":"telegram-sendphoto-by-url-failed-err","errorCode":null,"errorMessage":"Telegram sendPhoto by URL failed: {err}","messagePattern":"Telegram sendPhoto by URL failed: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/zeroclaw-channels/src/telegram.rs","lineNumber":3546,"sourceCode":"\n        if let Some(tid) = thread_id {\n            body[\"message_thread_id\"] = serde_json::Value::String(tid.to_string());\n        }\n\n        if let Some(cap) = caption {\n            body[\"caption\"] = serde_json::Value::String(cap.to_string());\n        }\n\n        let resp = self\n            .http_client()\n            .post(self.api_url(\"sendPhoto\"))\n            .json(&body)\n            .send()\n            .await?;\n\n        if !resp.status().is_success() {\n            let err = resp.text().await?;\n            anyhow::bail!(\"Telegram sendPhoto by URL 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, \"url\": url})),\n            \"photo (URL) sent to\"\n        );\n        Ok(())\n    }\n\n    /// Send a video by URL (Telegram will download it)\n    pub async fn send_video_by_url(\n        &self,\n        chat_id: &str,\n        thread_id: Option<&str>,\n        url: &str,\n        caption: Option<&str>,","sourceCodeStart":3528,"sourceCodeEnd":3564,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-channels/src/telegram.rs#L3528-L3564","documentation":"The URL variant of photo sending posts a JSON body (sendPhoto with a URL) and bails with Telegram's response body on non-2xx. Telegram's servers fetch and decode the image themselves, so rejections typically mean the URL did not yield a usable image.","triggerScenarios":"400 'failed to get HTTP URL content' when the URL returns HTML, a redirect chain, or an unsupported format (SVG, AVIF); 400 'wrong type of the current file' for non-image content; 403 chat not found/blocked; 429 rate limit.","commonSituations":"Chart/image URLs that 302 to a login page; webp variants Telegram's fetcher rejects; expired signed URLs; page URLs pasted instead of the direct image asset.","solutions":["curl -I the URL: expect 200 with image/jpeg or image/png content-type and no login redirect.","Use the direct asset URL; for protected hosts, download and use send_photo multipart instead.","Read the embedded `description` and match it against the URL's actual response.","On 429, honor retry_after and resend."],"exampleFix":null,"handlingStrategy":"fallback","validationCode":"let resp = reqwest::Client::new().head(url).send().await?;\nanyhow::ensure!(resp.status().is_success(), \"image URL not fetchable: {}\", resp.status());\nlet ct = resp.headers().get(reqwest::header::CONTENT_TYPE).and_then(|v| v.to_str().ok()).unwrap_or(\"\");\nanyhow::ensure!(ct.starts_with(\"image/\"), \"URL content-type is {ct}, not an image\");","typeGuard":"fn is_photo_url_failure(err: &anyhow::Error) -> bool {\n    let s = err.to_string();\n    s.contains(\"failed to get HTTP URL content\") || s.contains(\"wrong type of the current file\")\n}","tryCatchPattern":"if let Err(e) = channel.send_photo_by_url(chat, url).await {\n    if is_photo_url_failure(&e) {\n        let bytes = reqwest::get(url).await?.bytes().await?;\n        return channel.send_photo_bytes(chat, thread, bytes.to_vec(), \"photo.jpg\", None).await;\n    }\n    return Err(e);\n}","preventionTips":["Pass the direct asset URL, never an HTML page or redirect chain.","Verify content-type is image/jpeg|png|webp with a HEAD pre-check.","Keep a local-download-then-multipart fallback for protected hosts."],"tags":["telegram","sendphoto","send-by-url","bot-api","image"],"backgroundTag":"telegram-send-by-url-failed","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}