{"record":{"id":"b526c6943acac348","repo":"Hmbown/CodeWhale","slug":"host-cannot-be-empty","errorCode":null,"errorMessage":"host cannot be empty","messagePattern":"host cannot be empty","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"warning","filePath":"crates/tui/src/commands/groups/utility/network.rs","lineNumber":279,"sourceCode":"\nfn remove_host(list: &mut Vec<String>, host: &str) {\n    list.retain(|existing| normalize_host_for_compare(existing) != host);\n}\n\nfn normalize_host_arg(input: &str) -> anyhow::Result<String> {\n    let trimmed = input.trim();\n    let host = if trimmed.starts_with(\"http://\") || trimmed.starts_with(\"https://\") {\n        host_from_url(trimmed).context(\"URL must include a host\")?\n    } else {\n        if trimmed.contains(\"://\") || trimmed.contains('/') {\n            bail!(\"Pass a host like `github.com`, not a URL path\");\n        }\n        trimmed.to_string()\n    };\n\n    let normalized = normalize_host_for_compare(&host);\n    if normalized.is_empty() {\n        bail!(\"host cannot be empty\");\n    }\n    Ok(normalized)\n}\n\nfn normalize_host_for_compare(host: &str) -> String {\n    let trimmed = host.trim().trim_end_matches('.').to_ascii_lowercase();\n    if let Some(rest) = trimmed.strip_prefix(\"*.\") {\n        format!(\".{rest}\")\n    } else {\n        trimmed\n    }\n}\n\nfn display_list(values: &[String]) -> String {\n    if values.is_empty() {\n        \"[]\".to_string()\n    } else {\n        format!(\"[{}]\", values.join(\", \"))","sourceCodeStart":261,"sourceCodeEnd":297,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/0c42157ee52f9d55af2b506d71b46249910f77d3/crates/tui/src/commands/groups/utility/network.rs#L261-L297","documentation":"Bailed by `normalize_host_arg` when the argument normalizes to an empty string. `normalize_host_for_compare` trims whitespace, strips trailing dots, lowercases, and folds a `*.` prefix into a leading dot; if nothing remains the command refuses to add an empty entry to the host list.","triggerScenarios":"`/network allow .`, `/network deny \" \"`, `/network allow ...` — any argument that is only whitespace or dots after trimming. (An http(s) URL without a host hits the separate `URL must include a host` error instead.)","commonSituations":"Accidental empty paste from the clipboard, shell completion inserting a bare dot, or attempting to 'allow everything' by adding a dot-only entry.","solutions":["Pass an actual hostname such as `example.com`.","Use `*.example.com` to cover subdomains of a domain.","Re-run the command checking for stray quotes or trailing dots in the argument.","To change catch-all behavior use `/network default <allow|deny|prompt>`, never an empty host entry."],"exampleFix":"# before\n/network deny .\n# after\n/network deny example.com","handlingStrategy":"validation","validationCode":"fn normalized_host_or_none(input: &str) -> Option<String> {\n    let h = input.trim().trim_end_matches('.').to_ascii_lowercase();\n    if h.is_empty() {\n        return None;\n    }\n    Some(match h.strip_prefix(\"*.\") {\n        Some(rest) => format!(\".{rest}\"),\n        None => h,\n    })\n}","typeGuard":"fn is_nonempty_host(input: &str) -> bool {\n    !input.trim().trim_end_matches('.').is_empty()\n}","tryCatchPattern":null,"preventionTips":["Disable the submit action while the host input trims to empty.","Trim trailing dots from host input in the UI.","Treat 'block everything' as a default-policy change, never as an empty host entry."],"tags":["network","hostname","input-validation","tui"],"backgroundTag":"invalid-hostname","analyzedSha":"0c42157ee52f9d55af2b506d71b46249910f77d3","analyzedAt":"2026-08-20T21:50:45.477Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}