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

  1. 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.
  2. Fix the upstream producer of the URL so it emits `https://` links.
  3. 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

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


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