astrid-runtime/astrid · error
signed source URL cannot contain path segments
Error message
signed source URL cannot contain path segments
What it means
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.
Source
Thrown at crates/astrid-cli/src/commands/init_signed_source.rs:255
let manifest_path = Path::new(source);
if manifest_path.exists() && manifest_path.is_file() {
let path = manifest_path
.parent()
.ok_or_else(|| anyhow::anyhow!("Distro.toml has no parent directory"))?
.join(file_name);
return std::fs::read(&path)
.with_context(|| format!("failed to read signed source member {}", path.display()));
}
if offline {
bail!(
"--offline: signed source member {file_name} is not local and network access is forbidden"
);
}
let mut url = url::Url::parse(&super::resolve_distro_url(source)?)?;
url.path_segments_mut()
.map_err(|()| anyhow::anyhow!("signed source URL cannot contain path segments"))?
.pop()
.push(file_name);
fetch_url_bytes(url.as_str(), file_name, 1024 * 1024).await
}
async fn fetch_url_bytes(url: &str, name: &str, limit: usize) -> anyhow::Result<Vec<u8>> {
let client = reqwest::Client::builder()
.user_agent("astrid-cli")
.timeout(std::time::Duration::from_secs(30))
.build()?;
let response = client
.get(url)
.send()
.await
.with_context(|| format!("failed to fetch {name}"))?;
if !response.status().is_success() {
bail!(
"failed to fetch {name} from {url} (HTTP {})",View on GitHub (pinned to affd8760f4)
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
Example fix
// before let source = "astrid:mirror"; // cannot-be-a-base -> error // after let source = "https://mirror.example.com/distro/"; // path_segments_mut works
Defensive patterns
Strategy: validation
Validate before calling
let url = url::Url::parse(&resolve_distro_url(source)?)?;
if url.cannot_be_a_base() {
anyhow::bail!("distro source URL must be an http(s) URL with a host and path: {source}");
} Type guard
fn is_joinable_url(s: &str) -> bool {
url::Url::parse(s).map(|u| !u.cannot_be_a_base()).unwrap_or(false)
} Try / catch
match fetch_signed_member(source, offline, file_name).await {
Ok(bytes) => bytes,
Err(e) if e.to_string().contains("cannot contain path segments") => {
eprintln!("Bad distro source URL (needs scheme://host/path): {source}"); std::process::exit(2);
}
Err(e) => return Err(e),
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: "Invalid URL" errors: why new URL(), URI.parse, and reqwest::Url reject your string — missing scheme, whitespace, and bad path format — this error's family across 39 libraries.
Related errors
- capsule '{id}' provides an uplink but is absent from the ope
- CORS origin {raw:?} doesn't parse as a URL: {e}
- invalid value for {capsule_id}.{key}: expected one of {}, go
- no GitHub releases found for {org}/{repo}
- GitHub API rate limit exceeded - try again later
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/b8afc8a398fdea63.
Report an issue: GitHub.