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

  1. Confirm the release JSON asset objects include browser_download_url (curl the release endpoint and inspect).
  2. Remove any proxy/mirror that strips fields, or fetch releases directly from api.github.com.
  3. Re-run the release workflow so assets are published with complete metadata.
  4. 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

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


AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09). Data as JSON: /api/errors/f144a9894e40528e. Report an issue: GitHub.