nikivdev/code · error · anyhow::Error

Host must include a subdomain (for example: linsa.localhost)

Error message

Host must include a subdomain (for example: linsa.localhost)

What it means

Even a syntactically valid .localhost string is rejected if it has no actual subdomain: exactly ".localhost" or "localhost" would collide with the bare loopback name, so normalize_host requires a real subdomain part.

Source

Thrown at src/domains.rs:287

fn normalize_host(raw: &str) -> Result<String> {
    let mut host = raw.trim().to_ascii_lowercase();
    if let Some(stripped) = host.strip_prefix("http://") {
        host = stripped.to_string();
    } else if let Some(stripped) = host.strip_prefix("https://") {
        host = stripped.to_string();
    }
    host = host.trim_end_matches('/').to_string();
    if host.is_empty() {
        bail!("Host is empty");
    }
    if host.contains('/') || host.contains(':') || host.contains(char::is_whitespace) {
        bail!("Host must be a hostname like linsa.localhost");
    }
    if !host.ends_with(".localhost") {
        bail!("Host must end with .localhost");
    }
    if host == ".localhost" || host == "localhost" {
        bail!("Host must include a subdomain (for example: linsa.localhost)");
    }
    Ok(host)
}

fn normalize_target(raw: &str) -> Result<String> {
    let mut target = raw.trim().to_string();
    if let Some(stripped) = target.strip_prefix("http://") {
        target = stripped.to_string();
    } else if let Some(stripped) = target.strip_prefix("https://") {
        target = stripped.to_string();
    }
    target = target.trim_end_matches('/').to_string();
    if target.is_empty() {
        bail!("Target is empty");
    }
    if target.contains('/') || target.contains('?') || target.contains('#') {
        bail!("Target must be host:port");
    }

View on GitHub (pinned to a747e741ae)

Solutions

  1. Use a named subdomain like linsa.localhost or myapp.localhost
  2. Access bare localhost directly (not through this proxy) if that is the intent
  3. Update automation to derive a project-specific subdomain name

Example fix

// before
f domains add localhost :3000
// after
f domains add myapp.localhost :3000
Defensive patterns

Strategy: validation

Validate before calling

let h = raw.trim();
if h == "localhost" || h == ".localhost" {
    return Err("use a subdomain, e.g. linsa.localhost");
}

Type guard

fn has_localhost_subdomain(h: &str) -> bool {
    h.ends_with(".localhost")
        && h.strip_suffix(".localhost").map_or(false, |s| !s.is_empty())
}

Prevention

When it happens

Trigger: Passing "localhost" or ".localhost" to domains get/add/rm after the suffix check passes.

Common situations: Wanting to route the bare localhost name through the proxy, or writing scripts that default to "localhost" instead of a project-specific subdomain.

Related errors


AI-assisted analysis of nikivdev/code@a747e741ae (2026-09-01). Data as JSON: /api/errors/eef75c5b261f12c4. Report an issue: GitHub.