{"record":{"id":"c17969ab25643fc5","repo":"ducaale/xh","slug":"couldn-t-extract-host-from-url","errorCode":null,"errorMessage":"couldn't extract host from url","messagePattern":"couldn't extract host from url","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/session.rs","lineNumber":378,"sourceCode":"    }\n}\n\nfn xh_version() -> String {\n    if test_mode() {\n        \"0.0.0\".into()\n    } else {\n        env!(\"CARGO_PKG_VERSION\").into()\n    }\n}\n\nfn is_path(value: &OsString) -> bool {\n    value.to_string_lossy().contains(std::path::is_separator)\n}\n\nfn path_from_url(url: &Url) -> Result<String> {\n    match (url.host_str(), url.port()) {\n        (Some(\".\"), _) | (Some(\"..\"), _) | (None, _) => {\n            Err(anyhow!(\"couldn't extract host from url\"))\n        }\n        (Some(host), Some(port)) => Ok(format!(\"{host}_{port}\")),\n        (Some(host), None) => Ok(host.into()),\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    use anyhow::Result;\n    use reqwest::header::HeaderValue;\n\n    fn load_session_from_str(s: &str) -> Result<Session> {\n        Ok(Session {\n            url: Url::parse(\"http://example.net\")?,\n            content: serde_json::from_str::<Content>(s)?.migrate(),\n            path: PathBuf::new(),","sourceCodeStart":360,"sourceCodeEnd":396,"githubUrl":"https://github.com/ducaale/xh/blob/2404aceecc08b0b2d100fedc96f57745cd5904dc/src/session.rs#L360-L396","documentation":"When loading a named session, xh derives the session filename from the request URL's host (and optional port). If the Url has no host, or the host is '.' or '..', no usable filename can be formed, so path_from_url errors and load_session aborts.","triggerScenarios":"Calling load_session/--session with a URL whose host cannot be parsed, e.g. a relative URL without scheme (`xh --session=name :8080/path` variants), an empty or malformed URL, or a URL whose host normalizes to '.' or '..'.","commonSituations":"Forgetting the scheme in the URL so it parses as a path; programmatic use building Url from user input; automated scripts substituting empty host variables.","solutions":["Include scheme and host in the URL, e.g. https://api.example.com instead of a relative path","Validate the URL parses to a Some(host) before invoking with --session","Fix environment/variable substitution that leaves the host empty or '.'","If connecting to localhost, use http://localhost:8080 explicitly"],"exampleFix":"// before\nxh --session=mysess GET :8080/api/items\n// after\nxh --session=mysess GET http://localhost:8080/api/items","handlingStrategy":"validation","validationCode":"if let Ok(u) = Url::parse(url) {\n    if u.host_str().is_none() || matches!(u.host_str(), Some(\".\") | Some(\"..\")) {\n        eprintln!(\"URL must include a scheme and host for --session\");\n        std::process::exit(2);\n    }\n}","typeGuard":"fn url_has_host(u: &Url) -> bool {\n    u.host_str().map(|h| h != \".\" && h != \"..\").unwrap_or(false)\n}","tryCatchPattern":"match result {\n    Err(e) if e.to_string().contains(\"extract host from url\") => {\n        eprintln!(\"provide a full URL like https://host:port\");\n    }\n    r => r?,\n}","preventionTips":["Always pass absolute URLs (scheme + host) when using --session","Never interpolate empty variables into the URL position","Sanity-check URLs with a Url::parse + host check in scripts"],"tags":["cli","url","sessions"],"backgroundTag":"invalid-url-format","analyzedSha":"2404aceecc08b0b2d100fedc96f57745cd5904dc","analyzedAt":"2026-09-13T19:13:33.814Z","contentChangedAt":"2026-09-13T19:13:33.814Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}