Hmbown/CodeWhale · error

fleet alert URL from

Error message

fleet alert URL from {name} must use https

What it means

required_https_url() validates every Webhook alert URL with reqwest::Url::parse and then rejects any scheme other than https. This is a deliberate hardening step so alert payloads (which include run ids, status, and failure reasons) are never sent over plaintext HTTP where they could be read or tampered with. The error names the env var the URL came from.

Solutions

  1. Change the env var's value to an https:// URL (put a TLS terminator in front of local test receivers, e.g. an ngrok/local reverse proxy).
  2. Double-check the scheme spelling and that the value parses as an absolute URL — 'example.com/hook' without a scheme fails earlier with the not-a-valid-URL context error.
  3. For local testing keep dry_run=true, which validates config shape without sending.
  4. If you must target plain HTTP internally, that is intentionally unsupported — file the alert through a different mechanism rather than weakening the check.

Example fix

# before
$ export ALERT_HOOK_URL="http://127.0.0.1:9000/fleet"   # -> must use https

# after
$ export ALERT_HOOK_URL="https://hooks.internal.example.com/fleet"
Defensive patterns

Strategy: validation

Validate before calling

fn webhook_urls_are_https(cfg: &FleetAlertConfig) -> Result<(), String> {
    for (name, adapter) in &cfg.adapters {
        if let FleetAlertAdapterConfig::Webhook { url_env, .. } = adapter {
            if let Ok(url) = std::env::var(url_env) {
                if reqwest::Url::parse(&url).map(|u| u.scheme() != "https").unwrap_or(true) {
                    return Err(format!("adapter '{name}': {url_env} must be an https:// URL"));
                }
            }
        }
    }
    Ok(())
}

Type guard

fn is_https_url(s: &str) -> bool {
    reqwest::Url::parse(s).map(|u| u.scheme() == "https").unwrap_or(false)
}

Try / catch

Err(e) if e.to_string().contains("must use https") => {
    // value of the named env var is http:// — fix the env var; do not catch-and-send-over-http
}

Prevention

When it happens

Trigger: Setting the Webhook adapter's url_env to an http:// URL (e.g. a local test listener like http://127.0.0.1:9000/hook) and dispatching with dry_run=false; also URLs whose scheme parses oddly (e.g. 'localhost:8080/x' parses scheme 'localhost').

Common situations: Pointing alerts at a local dev receiver over plain HTTP; copying an internal http endpoint from docs; env vars provisioned by platforms that hand out http URLs by default; typos like missing '//' after https: are caught by the earlier parse error instead.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@0c42157ee5 (2026-08-20). Data as JSON: /api/errors/aab209b97c6be56a. Report an issue: GitHub.

Appendix: source

Thrown at crates/tui/src/fleet/alerts.rs:507

        .ok_or_else(|| anyhow!("fleet alert secret {name} is not configured"))
}

fn required_https_url<R>(resolver: &R, name: &str) -> Result<String>
where
    R: FleetAlertSecretResolver,
{
    let url = resolver
        .resolve(name)
        .ok_or_else(|| anyhow!("fleet alert URL {name} is not configured"))?;
    validate_https_alert_url(name, &url)?;
    Ok(url)
}

fn validate_https_alert_url(name: &str, url: &str) -> Result<()> {
    let parsed = reqwest::Url::parse(url)
        .with_context(|| format!("fleet alert URL from {name} is not a valid URL"))?;
    if parsed.scheme() != "https" {
        return Err(anyhow!("fleet alert URL from {name} must use https"));
    }
    Ok(())
}

fn short_reason(reason: &str) -> String {
    let trimmed = reason.trim();
    if trimmed.len() <= 240 {
        return trimmed.to_string();
    }
    let prefix: String = trimmed.chars().take(237).collect();
    format!("{prefix}...")
}

fn default_pagerduty_severity() -> String {
    "error".to_string()
}

#[cfg(test)]

View on GitHub (pinned to 0c42157ee5)