Hmbown/CodeWhale · error

outbound origin must be http or https

Error message

outbound origin must be http or https

What it means

validate_outbound_origin only accepts http/https schemes; any other scheme (file:, ftp:, ws:, etc.) is rejected before any request is built. This closes the SSRF door on non-HTTP transports for credential-bearing calls.

Solutions

  1. Change the configured origin to start with https:// (http is only useful for loopback in debug builds).
  2. Strip custom scheme prefixes (ws://, file://) from the config value before validation.
  3. Verify which env/config field you set — you may have populated the wrong variable.

Example fix

// before
export DAYTONA_API_URL=ftp://api.example.com
// after
export DAYTONA_API_URL=https://api.example.com
Defensive patterns

Strategy: validation

Validate before calling

if !raw.trim().starts_with("https://") { return Err("origin must start with https://"); }

Try / catch

if let Err(e) = validate_outbound_origin(raw) {
    if e.to_string().contains("http or https") {
        eprintln!("fix the scheme of the configured origin: {raw}");
    }
}

Prevention

When it happens

Trigger: Configuring an endpoint like file:///etc/passwd, ftp://host/x, or ws://... as the outbound origin (DAYTONA_API_URL, remote endpoint, or sandbox toolbox_url).

Common situations: Copy-pasting a websocket or internal file URL into a remote-endpoint setting; typo'd scheme like htp:// still parses via other schemes handling.

Understand the failure class

Background: "Invalid URL" / "URL cannot be empty": fix the malformed or missing URL behind request-construction failures — this error's family across 50 libraries.

Related errors


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

Appendix: source

Thrown at crates/tui/src/cloud_dispatch.rs:1264

///
/// Rules:
/// - `https` only for public hosts.
/// - explicit loopback hosts (`localhost`, `127.0.0.1`, `::1`) are allowed
///   only in debug builds, as the escape hatch for local smoke tests against
///   a self-hosted sandbox service; release builds reject them outright.
/// - the host must not be a private / link-local / reserved / multicast
///   address or a `.local` / `.internal` name, and no userinfo may ride
///   along.
///
/// DNS-resolved rebinding is out of scope and documented as such.
pub fn validate_outbound_origin(raw: &str) -> Result<reqwest::Url> {
    let trimmed = raw.trim();
    if trimmed.is_empty() || trimmed.len() > MAX_REMOTE_BYTES {
        bail!("outbound origin is empty or oversized");
    }
    let url = reqwest::Url::parse(trimmed).context("outbound origin is not a valid URL")?;
    if !matches!(url.scheme(), "http" | "https") {
        bail!("outbound origin must be http or https");
    }
    if !url.username().is_empty() || url.password().is_some() {
        bail!("outbound origin must not embed credentials");
    }
    let host = url
        .host_str()
        .context("outbound origin has no host")?
        .trim_end_matches('.')
        .to_ascii_lowercase();
    // `Url::host_str` keeps IPv6 brackets; strip them for the checks below.
    let host = host
        .strip_prefix('[')
        .and_then(|inner| inner.strip_suffix(']'))
        .map(str::to_string)
        .unwrap_or(host);
    let loopback_name = host == "localhost" || host == "127.0.0.1" || host == "::1";
    if loopback_name {
        if cfg!(debug_assertions) {

View on GitHub (pinned to 73e0f67d83)