{"record":{"id":"b8afc8a398fdea63","repo":"astrid-runtime/astrid","slug":"signed-source-url-cannot-contain-path-segments","errorCode":null,"errorMessage":"signed source URL cannot contain path segments","messagePattern":"signed source URL cannot contain path segments","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-cli/src/commands/init_signed_source.rs","lineNumber":255,"sourceCode":"    let manifest_path = Path::new(source);\n    if manifest_path.exists() && manifest_path.is_file() {\n        let path = manifest_path\n            .parent()\n            .ok_or_else(|| anyhow::anyhow!(\"Distro.toml has no parent directory\"))?\n            .join(file_name);\n        return std::fs::read(&path)\n            .with_context(|| format!(\"failed to read signed source member {}\", path.display()));\n    }\n\n    if offline {\n        bail!(\n            \"--offline: signed source member {file_name} is not local and network access is forbidden\"\n        );\n    }\n\n    let mut url = url::Url::parse(&super::resolve_distro_url(source)?)?;\n    url.path_segments_mut()\n        .map_err(|()| anyhow::anyhow!(\"signed source URL cannot contain path segments\"))?\n        .pop()\n        .push(file_name);\n    fetch_url_bytes(url.as_str(), file_name, 1024 * 1024).await\n}\n\nasync fn fetch_url_bytes(url: &str, name: &str, limit: usize) -> anyhow::Result<Vec<u8>> {\n    let client = reqwest::Client::builder()\n        .user_agent(\"astrid-cli\")\n        .timeout(std::time::Duration::from_secs(30))\n        .build()?;\n    let response = client\n        .get(url)\n        .send()\n        .await\n        .with_context(|| format!(\"failed to fetch {name}\"))?;\n    if !response.status().is_success() {\n        bail!(\n            \"failed to fetch {name} from {url} (HTTP {})\",","sourceCodeStart":237,"sourceCodeEnd":273,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-cli/src/commands/init_signed_source.rs#L237-L273","documentation":"fetch_signed_member builds the remote URL for a signed-source member (Distro.lock or its .sig) by taking the distro URL, popping its last path segment, and appending the member file name. `Url::path_segments_mut()` returns Err(()) only when the URL cannot have path segments — i.e. it is a cannot-be-a-base URL (no hierarchical path, e.g. `mailto:` or a URL with an empty host/opaque path). The library throws this because it cannot safely join a file name onto such a URL.","triggerScenarios":"Calling fetch_signed_manifest (which calls fetch_signed_member) when `source` resolves, via resolve_distro_url, to a cannot-be-a-base URL such as a malformed scheme-only URL (e.g. `astrid:` with no path/host) or an opaque URL. A well-formed http(s) URL never triggers this.","commonSituations":"A typo'd or hand-edited distro source in Distro.toml or CLI config where the URL lacks a host (e.g. `https:/mirror.example/distro/` missing a slash, or a bare scheme), producing an opaque/cannot-be-a-base URL.","solutions":["Fix the distro source URL so it is a standard http(s) URL with scheme://host/path (e.g. `https://mirror.example/distro/`)","Run the URL through url::Url::parse and check `url.cannot_be_a_base()` before fetching to fail fast with a clearer message","Verify resolve_distro_url output for the given source value; if a config mapping produced the bad URL, correct the mapping"],"exampleFix":"// before\nlet source = \"astrid:mirror\"; // cannot-be-a-base -> error\n// after\nlet source = \"https://mirror.example.com/distro/\"; // path_segments_mut works","handlingStrategy":"validation","validationCode":"let url = url::Url::parse(&resolve_distro_url(source)?)?;\nif url.cannot_be_a_base() {\n    anyhow::bail!(\"distro source URL must be an http(s) URL with a host and path: {source}\");\n}","typeGuard":"fn is_joinable_url(s: &str) -> bool {\n    url::Url::parse(s).map(|u| !u.cannot_be_a_base()).unwrap_or(false)\n}","tryCatchPattern":"match fetch_signed_member(source, offline, file_name).await {\n    Ok(bytes) => bytes,\n    Err(e) if e.to_string().contains(\"cannot contain path segments\") => {\n        eprintln!(\"Bad distro source URL (needs scheme://host/path): {source}\"); std::process::exit(2);\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Always use fully-qualified http(s) URLs (scheme://host/path) for distro sources","Validate distro source config at startup with url::Url::parse + cannot_be_a_base check","Prefer the local-file source path when the lock/sig files are already on disk"],"tags":["url","network","config"],"backgroundTag":"invalid-url-format","analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}