Hmbown/CodeWhale · error · anyhow::Error

Codewhale account API base URL must use HTTPS (loopback…

Error message

Codewhale account API base URL must use HTTPS (loopback HTTP is allowed for testing)

What it means

For security the account API must be reached over TLS; validate_api_base requires https, with one carve-out: plain http is accepted only when the host is loopback (localhost/127.0.0.1/::1) for local testing. Any non-loopback http URL is rejected so tokens are never sent in cleartext over a network.

Solutions

  1. Use https:// for the endpoint (Let's Encrypt or your internal CA).
  2. For local testing only, use a loopback host: http://localhost:8080 or http://127.0.0.1:8080.
  3. If a LAN hostname is required, put TLS in front via a reverse proxy and use its https origin.

Example fix

// before
CODEWHALE_API_BASE=http://staging.internal:8080
// after
CODEWHALE_API_BASE=https://staging.internal
// or, local testing only:
CODEWHALE_API_BASE=http://127.0.0.1:8080
Defensive patterns

Strategy: validation

Validate before calling

fn is_https_or_loopback(url: &str) -> bool {
    url::Url::parse(url.trim()).map_or(false, |u| {
        let host = u.host_str().unwrap_or("");
        u.scheme() == "https"
            || (u.scheme() == "http"
                && (host == "localhost" || host.starts_with("127.")))
    })
}

Prevention

When it happens

Trigger: Setting the base URL to http://api.example.com or http://<lan-ip>:port — any http URL whose host is not loopback.

Common situations: Pointing at an internal staging server without TLS; self-hosting behind plain http on a LAN; skipping the reverse-proxy TLS step.

Understand the failure class

Background: "Invalid URL" errors: why new URL(), URI.parse, and reqwest::Url reject your string — missing scheme, whitespace, and bad path format — this error's family across 39 libraries.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/239a306d0e33be3b. Report an issue: GitHub.

Appendix: source

Thrown at crates/cli/src/cloud.rs:1031

}

fn validate_api_base(value: &str) -> Result<ValidatedApiBase> {
    let mut url = Url::parse(value.trim()).context("invalid Codewhale account API base URL")?;
    if !url.username().is_empty() || url.password().is_some() {
        bail!("Codewhale account API base URL must not contain credentials");
    }
    if url.query().is_some() || url.fragment().is_some() {
        bail!("Codewhale account API base URL must not contain a query or fragment");
    }
    if !matches!(url.path(), "" | "/") {
        bail!("Codewhale account API base URL must be an origin without a path");
    }
    let host = url
        .host_str()
        .ok_or_else(|| anyhow!("Codewhale account API base URL must include a host"))?;
    let allowed = url.scheme() == "https" || (url.scheme() == "http" && is_loopback_host(host));
    if !allowed {
        bail!(
            "Codewhale account API base URL must use HTTPS (loopback HTTP is allowed for testing)"
        );
    }
    url.set_path("/");
    let display = url.as_str().trim_end_matches('/').to_string();
    Ok(ValidatedApiBase { url, display })
}

fn validate_verification_url(
    value: &str,
    api_base: &str,
    user_code: &str,
    complete: bool,
) -> Result<String> {
    let url =
        Url::parse(value).context("The Codewhale service returned an invalid verification URL")?;
    if value != url.as_str() {
        bail!("The Codewhale service returned an unsafe verification URL");

View on GitHub (pinned to 73e0f67d83)