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

A hard security guard in the WhatsApp channel: ensure_https requires the configured API base URL to start with https://, and every network call (post_message, send_interactive_buttons, send_interactive_list, health_check) passes through it. It refuses to transmit tokens and message payloads over plaintext HTTP.

Source

Thrown at crates/zeroclaw-channels/src/whatsapp.rs:160

            zeroclaw_api::channel::AttributedApprovalResponse::from_runtime(
                ChannelApprovalResponse::Deny,
                zeroclaw_api::channel::ApprovalSource::Unreachable,
            )
        }
        Err(_) => {
            remove_pending_and_disarm(&token, &mut guard).await;
            zeroclaw_api::channel::AttributedApprovalResponse::from_runtime(
                ChannelApprovalResponse::Deny,
                zeroclaw_api::channel::ApprovalSource::TimedOut,
            )
        }
    };
    Ok(attributed)
}

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(())
}

pub struct WhatsAppChannel {
    access_token: String,
    endpoint_id: String,
    verify_token: String,
    /// The alias key under `[channels.whatsapp.<alias>]` this handle is
    /// bound to. Used to scope peer-group writes and resolver lookups.
    alias: String,
    /// Resolves inbound external peers from canonical state at message-time.
    /// No cache (see AGENTS.md "ABSOLUTE RULE — SINGLE SOURCE OF TRUTH").
    peer_resolver: Arc<dyn Fn() -> Vec<String> + Send + Sync>,
    /// Per-channel proxy URL override.
    proxy_url: Option<String>,

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Change base_url to https:// — for local bridges, front them with a TLS-terminating reverse proxy (Caddy/nginx) or a TLS tunnel
  2. Do not bypass the guard: it protects auth tokens and message content from network sniffing
  3. Check the URL for a missing scheme, http typo, or stray whitespace before it

Example fix

# before
base_url = "http://localhost:3000"
# after (TLS-terminating local proxy in front of the bridge)
base_url = "https://localhost:8443"
Defensive patterns

Strategy: validation

Validate before calling

fn is_https_url(url: &str) -> bool {
    url.trim_start().starts_with("https://")
}
if !is_https_url(&cfg.base_url) {
    return Err(anyhow::anyhow!("WhatsApp base_url must use https://"));
}

Type guard

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

Try / catch

Do not catch-and-continue: this is a deliberate security refusal. Let it fail channel startup, fix the URL to https://, and put TLS in front of any local bridge.

Prevention

When it happens

Trigger: Configuring the WhatsApp API base_url with http:// (e.g., a local bridge at http://localhost:3000) or omitting/mistyping the scheme so it fails the https:// prefix check, then invoking send or health_check.

Common situations: Local development against a plain-HTTP WhatsApp bridge (WaAPI-style); port-forwards or tunnels without TLS; a missing scheme or leading whitespace in the URL.

Related errors


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