zeroclaw-labs/zeroclaw · error

Refusing to transmit sensitive data over non-HTTPS URL: URL

Error message

Refusing to transmit sensitive data over non-HTTPS URL: URL scheme must be https

What it means

ensure_https is the guard run by upload_media, send_media_message, and send_text_markdown before any QQ HTTP call; it rejects every URL that does not start with "https://" because these requests carry the app access token and user media. It exists so a misconfigured or tampered base/media URL can never downgrade credentials to plaintext HTTP. It is a deliberate refusal, not a network failure.

Source

Thrown at crates/zeroclaw-channels/src/qq.rs:72

    voice_dedup_parts: Vec<QQVoiceDedupPart>,
}

/// Response from QQ media upload API.
#[derive(Debug, Deserialize)]
struct QQUploadResponse {
    file_info: String,
    ttl: Option<u64>,
}

/// Cached upload entry to avoid re-uploading the same file within TTL.
struct UploadCacheEntry {
    file_info: String,
    expires_at: u64,
}

fn ensure_https(url: &str) -> anyhow::Result<()> {
    if !url.starts_with("https://") {
        anyhow::bail!(
            "Refusing to transmit sensitive data over non-HTTPS URL: URL scheme must be https"
        );
    }
    Ok(())
}

/// Check whether a file extension is a natively supported QQ voice format.
fn is_native_voice_ext(ext: &str) -> bool {
    matches!(ext.to_ascii_lowercase().as_str(), "wav" | "mp3" | "silk")
}

fn has_supported_transcription_extension(filename: &str) -> bool {
    let ext = Path::new(filename)
        .extension()
        .and_then(|e| e.to_str())
        .unwrap_or("")
        .to_ascii_lowercase();

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Change the offending URL to https:// (real QQ endpoints api.sgroup.qq.com and bots.qq.com are https)
  2. For local tests, use a TLS-capable mock server or the platform's https test endpoint instead of plain http
  3. Audit any api-base/media URL override fields in [channels.qq.*] and remove http:// values

Example fix

# before
[channels.qq.main]
api_base = "http://qq-mock.internal:8080"   # -> bail: non-HTTPS URL refused

# after
[channels.qq.main]
api_base = "https://qq-mock.internal:8443"
Defensive patterns

Strategy: validation

Validate before calling

// Before invoking QQ send paths, apply the same rule the library enforces:
fn qq_urls_are_https(urls: &[&str]) -> anyhow::Result<()> {
    for u in urls {
        if !u.starts_with("https://") {
            anyhow::bail!("refusing non-HTTPS QQ URL: {u}");
        }
    }
    Ok(())
}
qq_urls_are_https(&[api_base, media_url])?;

Type guard

fn is_https(url: &str) -> bool {
    url.starts_with("https://")
}

assert!(is_https("https://api.sgroup.qq.com"));
assert!(!is_https("http://qq-mock.internal:8080"));

Try / catch

match ch.send(&msg).await {
    Err(e) if e.to_string().contains("non-HTTPS URL") => {
        // Deterministic security refusal: fix the URL source (config override / proxy); never downgrade.
    }
    rest => rest?,
}

Prevention

When it happens

Trigger: Any URL reaching the QQ HTTP helpers configured or overridden as http:// — e.g. an api base override for testing, a proxy rewriting URLs, or a media URL from a marker like [IMAGE:http://...] where the value is used as an endpoint rather than a fetch target.

Common situations: Pointing the channel at an http:// mock server in tests (use an https test server instead); corporate proxies that hand back http:// URLs; copy-pasting an internal http endpoint into a field that is used for authenticated API calls.

Related errors


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