Hmbown/CodeWhale · warning · anyhow::Error

Pass a host like `github.com`, not a URL path

Error message

Pass a host like `github.com`, not a URL path

What it means

`normalize_host_arg` validates the host argument of the `/network allow|deny <host>` commands. Inputs starting with `http://` or `https://` are reduced to their host via `host_from_url`; any other input containing `://` (ftp://, ssh://, git://) or a `/` path component bails, because the network policy list stores bare hosts, not URLs.

Source

Thrown at crates/tui/src/commands/groups/utility/network.rs:272

    if !list
        .iter()
        .any(|existing| normalize_host_for_compare(existing) == host)
    {
        list.push(host.to_string());
    }
}

fn remove_host(list: &mut Vec<String>, host: &str) {
    list.retain(|existing| normalize_host_for_compare(existing) != host);
}

fn normalize_host_arg(input: &str) -> anyhow::Result<String> {
    let trimmed = input.trim();
    let host = if trimmed.starts_with("http://") || trimmed.starts_with("https://") {
        host_from_url(trimmed).context("URL must include a host")?
    } else {
        if trimmed.contains("://") || trimmed.contains('/') {
            bail!("Pass a host like `github.com`, not a URL path");
        }
        trimmed.to_string()
    };

    let normalized = normalize_host_for_compare(&host);
    if normalized.is_empty() {
        bail!("host cannot be empty");
    }
    Ok(normalized)
}

fn normalize_host_for_compare(host: &str) -> String {
    let trimmed = host.trim().trim_end_matches('.').to_ascii_lowercase();
    if let Some(rest) = trimmed.strip_prefix("*.") {
        format!(".{rest}")
    } else {
        trimmed
    }

View on GitHub (pinned to 0c42157ee5)

Solutions

  1. Pass the bare host: `/network deny github.com`.
  2. If you paste an http(s) URL the host is extracted automatically, but a bare host is the canonical form.
  3. To cover a domain and all its subdomains use wildcard syntax `*.example.com`.
  4. Check the argument for `/` or a non-HTTP `://` scheme before submitting.

Example fix

# before
/network deny https://github.com/codewhale/codewhale
# after
/network deny github.com
Defensive patterns

Strategy: validation

Validate before calling

fn normalize_host_arg(input: &str) -> Option<String> {
    let t = input.trim();
    let host = if t.starts_with("http://") || t.starts_with("https://") {
        reqwest::Url::parse(t).ok()?.host_str()?.to_ascii_lowercase()
    } else {
        if t.contains("://") || t.contains('/') {
            return None;
        }
        t.to_string()
    };
    let host = host.trim_end_matches('.').to_ascii_lowercase();
    (!host.is_empty()).then_some(host)
}

Type guard

fn is_bare_host(input: &str) -> bool {
    let t = input.trim();
    !t.is_empty() && !t.contains('/') && !t.contains("://")
}

Prevention

When it happens

Trigger: `/network deny github.com/org/repo` (URL path), `/network allow ftp://mirror.example.org`, `/network deny api.example.com/v1/entities`.

Common situations: Pasting a full git clone URL or API endpoint URL instead of the domain; using ssh:// or git:// scheme URLs, which the parser deliberately refuses (only http(s) is unwrapped).

Related errors


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