{"record":{"id":"eef75c5b261f12c4","repo":"nikivdev/code","slug":"host-must-include-a-subdomain-for-example-linsa","errorCode":null,"errorMessage":"Host must include a subdomain (for example: linsa.localhost)","messagePattern":"Host must include a subdomain \\(for example: linsa\\.localhost\\)","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"src/domains.rs","lineNumber":287,"sourceCode":"fn normalize_host(raw: &str) -> Result<String> {\n    let mut host = raw.trim().to_ascii_lowercase();\n    if let Some(stripped) = host.strip_prefix(\"http://\") {\n        host = stripped.to_string();\n    } else if let Some(stripped) = host.strip_prefix(\"https://\") {\n        host = stripped.to_string();\n    }\n    host = host.trim_end_matches('/').to_string();\n    if host.is_empty() {\n        bail!(\"Host is empty\");\n    }\n    if host.contains('/') || host.contains(':') || host.contains(char::is_whitespace) {\n        bail!(\"Host must be a hostname like linsa.localhost\");\n    }\n    if !host.ends_with(\".localhost\") {\n        bail!(\"Host must end with .localhost\");\n    }\n    if host == \".localhost\" || host == \"localhost\" {\n        bail!(\"Host must include a subdomain (for example: linsa.localhost)\");\n    }\n    Ok(host)\n}\n\nfn normalize_target(raw: &str) -> Result<String> {\n    let mut target = raw.trim().to_string();\n    if let Some(stripped) = target.strip_prefix(\"http://\") {\n        target = stripped.to_string();\n    } else if let Some(stripped) = target.strip_prefix(\"https://\") {\n        target = stripped.to_string();\n    }\n    target = target.trim_end_matches('/').to_string();\n    if target.is_empty() {\n        bail!(\"Target is empty\");\n    }\n    if target.contains('/') || target.contains('?') || target.contains('#') {\n        bail!(\"Target must be host:port\");\n    }","sourceCodeStart":269,"sourceCodeEnd":305,"githubUrl":"https://github.com/nikivdev/code/blob/a747e741ae92c09071d0ae946ab48488adcff1ce/src/domains.rs#L269-L305","documentation":"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.","triggerScenarios":"Passing \"localhost\" or \".localhost\" to domains get/add/rm after the suffix check passes.","commonSituations":"Wanting to route the bare localhost name through the proxy, or writing scripts that default to \"localhost\" instead of a project-specific subdomain.","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"],"exampleFix":"// before\nf domains add localhost :3000\n// after\nf domains add myapp.localhost :3000","handlingStrategy":"validation","validationCode":"let h = raw.trim();\nif h == \"localhost\" || h == \".localhost\" {\n    return Err(\"use a subdomain, e.g. linsa.localhost\");\n}","typeGuard":"fn has_localhost_subdomain(h: &str) -> bool {\n    h.ends_with(\".localhost\")\n        && h.strip_suffix(\".localhost\").map_or(false, |s| !s.is_empty())\n}","tryCatchPattern":null,"preventionTips":["Never route bare localhost through the proxy","Generate project-specific subdomains in scripts","Document the subdomain requirement in your dev setup docs"],"tags":["validation","domains","naming-convention"],"backgroundTag":"invalid-input","analyzedSha":"a747e741ae92c09071d0ae946ab48488adcff1ce","analyzedAt":"2026-09-01T22:43:55.719Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}