astrid-runtime/astrid · error

release has no asset list

Error message

release has no asset list

What it means

exact_asset_url looks up a named asset in a GitHub release JSON object. If the release payload has no 'assets' array (missing field or non-array), it throws this error. It guards against malformed or unexpected release responses before any asset matching happens.

Source

Thrown at crates/astrid-cli/src/commands/self_update/mod.rs:308

    }
    if method.manages_own_binary() {
        return UpdatePlan::DeferToManager {
            manager: method.label(),
            how: method.upgrade_command(channel),
        };
    }
    UpdatePlan::ApplyInPlace
}

/// Find exactly one release asset and return its browser download URL.
pub(super) fn exact_asset_url<'a>(
    release: &'a serde_json::Value,
    name: &str,
) -> anyhow::Result<&'a str> {
    let assets = release
        .get("assets")
        .and_then(serde_json::Value::as_array)
        .ok_or_else(|| anyhow::anyhow!("release has no asset list"))?;
    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())

View on GitHub (pinned to affd8760f4)

Solutions

  1. Verify the fetched release is a real GitHub release JSON containing an 'assets' array (curl the releases/tag/<tag> endpoint).
  2. Check for GitHub API rate limiting (403 with rate-limit body) or auth problems and add a token if needed.
  3. Ensure the release has had its build artifacts uploaded; re-run the release workflow if assets are missing.
  4. Bypass any proxy/mirror that strips or reshapes the assets field.
Defensive patterns

Strategy: type-guard

Validate before calling

// before trusting the release payload
const release = JSON.parse(body);
if (!Array.isArray(release.assets)) throw new Error("response is not a release object");

Type guard

fn is_release_json(v: &serde_json::Value) -> bool {
    v.get("assets").and_then(serde_json::Value::as_array).is_some()
}

Try / catch

let assets = release.get("assets")
    .and_then(serde_json::Value::as_array)
    .ok_or_else(|| anyhow::anyhow!("release has no asset list"))
    .with_context(|| format!("release payload: {}", release))?;

Prevention

When it happens

Trigger: fetch_release_by_tag or another release fetch returns JSON lacking an 'assets' array — e.g. a GitHub API error body, a draft release with no assets, or a proxy/mirror returning reshaped JSON — then any consumer (publisher_bundle_url, integrity_manifest_url, download_verify_extract, resolve_signed_channel, resolve_target_blake3) calls exact_asset_url.

Common situations: GitHub API rate-limit or error JSON being parsed as if it were a release; querying a draft/prerelease that has no assets uploaded yet; a corporate proxy that rewrites API 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/8a913b9303600bb6. Report an issue: GitHub.