zeroclaw-labs/zeroclaw · error · anyhow::Error
refusing non-HTTPS attachment URL: {url}
Error message
refusing non-HTTPS attachment URL: {url} What it means
`download_remote_attachment` fetches remote WeChat attachments but hard-refuses any URL whose scheme is not `https://`. The check runs before any network I/O, so this is a policy rejection, not a network failure. It exists to guarantee that file content and the AES-encrypted upload credentials never travel over plaintext HTTP.
Source
Thrown at crates/zeroclaw-channels/src/wechat.rs:1173
.and_then(|value| value.split(';').next())
.and_then(mime_guess::get_mime_extensions_str)
.and_then(|exts: &[&str]| exts.first().copied())
.unwrap_or(kind.default_extension());
format!(
"wechat_attachment_{}.{}",
uuid::Uuid::new_v4().simple(),
ext
)
}
async fn download_remote_attachment(
&self,
url: &str,
kind: WeChatAttachmentKind,
) -> anyhow::Result<WeChatMediaPayload> {
if !url.starts_with("https://") {
anyhow::bail!("refusing non-HTTPS attachment URL: {url}");
}
let resp = self
.client
.get(url)
.timeout(API_TIMEOUT)
.send()
.await
.with_context(|| format!("attachment download failed: {url}"))?;
if !resp.status().is_success() {
let status = resp.status();
let body = resp.text().await.unwrap_or_default();
anyhow::bail!("attachment download failed ({status}): {body}");
}
if let Some(len) = resp.content_length()
&& len > WECHAT_MEDIA_MAX_BYTES
{View on GitHub (pinned to 88bb9c8533)
Solutions
- Serve the file over HTTPS — put a TLS terminator (Caddy, nginx, or a tunnel like ngrok/localtunnel) in front of the HTTP service and use the `https://` URL.
- Fix the upstream producer of the URL so it emits `https://` links.
- If the content must come from a plain-HTTP internal host, download it yourself into `workspace_dir` and attach it as a local file path instead of a URL.
Example fix
# before attachment.target = "http://localhost:8000/report.pdf" # -> refusing non-HTTPS attachment URL # after (option 1: TLS in front) attachment.target = "https://files.example.com/report.pdf" # after (option 2: local file inside workspace) curl -o /workspace/report.pdf http://localhost:8000/report.pdf attachment.target = "report.pdf"
Defensive patterns
Strategy: validation
Validate before calling
// enforce the scheme at the source, before building the attachment
fn https_only(url: &str) -> anyhow::Result<&str> {
if url.starts_with("https://") {
Ok(url)
} else {
anyhow::bail!("attachment URL must use https: {url}")
}
}
let target = https_only(&attachment_url)?; Type guard
fn is_https_attachment_url(url: &str) -> bool {
url.starts_with("https://")
} Try / catch
match channel.send(msg_with_attachment(url)).await {
Err(err) if err.to_string().contains("refusing non-HTTPS attachment URL") => {
// download via your own TLS path into the workspace, then attach locally
let local = mirror_into_workspace(&url).await?; // your https-capable fetcher
channel.send(msg_with_attachment(&local)).await?;
}
other => other?,
} Prevention
- Validate `target.starts_with("https://")` wherever attachment URLs enter your system (user input, scraped HTML, feeds).
- For local dev servers, front them with a TLS proxy or tunnel so emitted links are already https.
- Do not try to smuggle http URLs past the check by scheme trickery; the channel refuses them before any request by design.
When it happens
Trigger: Passing a WeChat attachment target starting with `http://` (or any other non-`https://` scheme that `is_remote_url` classifies as remote) to a send call, which routes to `download_remote_attachment` via `load_attachment_payload`. Examples: `http://cdn.example.com/file.png`, `http://localhost:8000/report.pdf`, `ftp://...` style remote targets.
Common situations: Local development servers expose files over plain HTTP (`http://localhost:8000`) and get wired into attachment targets; internal tooling emits `http://` intranet CDN links; a typo or lowercased scheme variant; third-party feeds that still return http links; forwarding emails/chat messages whose embedded media links are http.
Related errors
- attachment path {} canonicalizes to {} which escapes workspa
- attachment path {} escapes workspace {}
- attachment download failed ({status}): {body}
- attachment Content-Length ({len} bytes) exceeds {} MB limit
- attachment exceeds {} MB limit
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/aac5a80ec65debff.
Report an issue: GitHub.