astrid-runtime/astrid · error
release asset '{name}' has no download URL
Error message
release asset '{name}' has no download URL What it means
The final step of exact_asset_url extracts the asset's browser_download_url, requiring a non-empty string. If the matched asset object lacks that field, has a non-string value, or an empty URL, it throws this error. It prevents proceeding with a download target that doesn't exist.
Source
Thrown at crates/astrid-cli/src/commands/self_update/mod.rs:327
anyhow::ensure!(
assets.len() <= MAX_RELEASE_ASSETS,
"release contains too many assets"
);
let mut matches = assets
.iter()
.filter(|asset| asset.get("name").and_then(|value| value.as_str()) == Some(name));
let asset = matches
.next()
.ok_or_else(|| anyhow::anyhow!("release has no asset '{name}'"))?;
anyhow::ensure!(
matches.next().is_none(),
"release contains duplicate asset '{name}'"
);
asset
.get("browser_download_url")
.and_then(|value| value.as_str())
.filter(|url| !url.is_empty())
.ok_or_else(|| anyhow::anyhow!("release asset '{name}' has no download URL"))
}
fn publisher_bundle_url<'a>(
release: &'a serde_json::Value,
archive_name: &str,
) -> Result<&'a str, UpdateStageError> {
let bundle_name = format!("{archive_name}.sigstore.json");
exact_asset_url(release, &bundle_name)
.map_err(|error| UpdateStageError::publisher(error.to_string()))
}
fn integrity_manifest_url(release: &serde_json::Value) -> Result<&str, UpdateStageError> {
exact_asset_url(release, "BLAKE3SUMS.txt")
.map_err(|error| UpdateStageError::integrity(error.to_string()))
}
/// Stream a URL into memory under the size cap.
pub(super) async fn download_bounded(View on GitHub (pinned to affd8760f4)
Solutions
- Confirm the release JSON asset objects include browser_download_url (curl the release endpoint and inspect).
- Remove any proxy/mirror that strips fields, or fetch releases directly from api.github.com.
- Re-run the release workflow so assets are published with complete metadata.
- If a GitHub API change is suspected, upgrade the CLI to a version matching the current API shape.
Defensive patterns
Strategy: type-guard
Validate before calling
// validate asset shape before consuming
const url = asset.browser_download_url;
if (typeof url !== "string" || url.length === 0) throw new Error("asset missing download URL"); Type guard
fn has_download_url(asset: &serde_json::Value) -> bool {
asset.get("browser_download_url")
.and_then(|v| v.as_str())
.map(|s| !s.is_empty())
.unwrap_or(false)
} Prevention
- Fetch releases directly from api.github.com, not through field-dropping proxies.
- Validate release JSON shape once at fetch time before asset lookups.
When it happens
Trigger: A release asset entry in the fetched JSON has no browser_download_url — e.g. assets from a non-GitHub mirror, partially populated API responses, or empty-string URLs — after the name matched successfully.
Common situations: Custom update source (ASTRID_UPDATE_REPO) proxied through an API gateway that drops or renames fields; hand-rolled release JSON fixtures; GitHub API shape changes or partial responses.
Understand the failure class
Background: "invalid response format", "malformed payload", "missing data field": when an API returns 200 but the response shape is wrong — this error's family across 23 libraries.
Related errors
- release has no asset list
- release contains too many assets
- release has no asset '{name}'
- release contains duplicate asset '{name}'
- release {resolved_ref} of {org}/{repo} ships no .capsule ass
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/f144a9894e40528e.
Report an issue: GitHub.