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
- Use a named subdomain like linsa.localhost or myapp.localhost
- Access bare localhost directly (not through this proxy) if that is the intent
- 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
- Never route bare localhost through the proxy
- Generate project-specific subdomains in scripts
- Document the subdomain requirement in your dev setup docs
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
- Host must end with .localhost
- Host is empty
- Host must be a hostname like linsa.localhost
- lifecycle.domains.aliases[].host is required
- lifecycle.domains.aliases[].target is required
AI-assisted analysis of nikivdev/code@a747e741ae (2026-09-01).
Data as JSON: /api/errors/eef75c5b261f12c4.
Report an issue: GitHub.