astrid-runtime/astrid · error

failed to fetch {name} from {url} (HTTP {})

Error message

failed to fetch {name} from {url} (HTTP {})

What it means

This error is raised by fetch_url_bytes in init_signed_source.rs when an HTTP GET for a signed-distribution resource completes but returns a non-2xx status. The library bails with the resource name, its URL, and the HTTP status so the user knows exactly which remote artifact could not be downloaded during `astrid init` with a signed source. It guards the trust chain: manifests, signatures, and capsule members must all be fetched successfully before verification can proceed.

Source

Thrown at crates/astrid-cli/src/commands/init_signed_source.rs:272

    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 {})",
            response.status()
        );
    }
    let mut bytes = Vec::new();
    let mut response = response;
    while let Some(chunk) = response.chunk().await? {
        bytes.extend_from_slice(&chunk);
        anyhow::ensure!(bytes.len() <= limit, "{name} exceeds size limit");
    }
    Ok(bytes)
}

/// Bind exact TOML bytes into the signed lock, then verify that lock.
fn verify_signed_manifest(
    home: &AstridHome,
    manifest: &DistroManifest,
    manifest_hash: &str,

View on GitHub (pinned to affd8760f4)

Solutions

  1. Open the URL from the error message in a browser or `curl -I` to see the actual status and fix the cause (404: correct the URL or tag; 403/401: add authentication or make the artifact public).
  2. Check network/proxy configuration — corporate proxies or firewalls may return 403/502 for the host; configure HTTPS_PROXY or allowlist the domain.
  3. Re-run the command; 429/5xx are often transient, so retry after a backoff or pin to a mirror.
  4. If the upstream project restructured its repo, update the capsule source URLs in Distro.toml/Distro.lock to the new location.

Example fix

// before (Distro.toml capsule source)
source = "https://example.com/distros/mydistro/capsules/foo-1.0.0.capsule"
// after (correct tag/path after upstream re-tag)
source = "https://example.com/distros/mydistro/v1.2/capsules/foo-1.0.0.capsule"
Defensive patterns

Strategy: retry

Validate before calling

let url: Url = source.parse()?;
let head = client.head(url.clone()).send().await?;
if !head.status().is_success() {
    eprintln!("artifact unreachable: {} -> {}", url, head.status());
}

Type guard

fn is_ok_response(resp: &reqwest::Response) -> bool { resp.status().is_success() }

Try / catch

match fetch_url_bytes(&client, &name, &url).await {
    Ok(bytes) => bytes,
    Err(e) if e.to_string().contains("HTTP 429") || e.to_string().contains("HTTP 5") => {
        tokio::time::sleep(Duration::from_secs(5)).await;
        retry_with_backoff(|| fetch_url_bytes(&client, &name, &url), 3).await?
    }
    Err(e) => return Err(e.context("check the URL and network/proxy settings")),
}

Prevention

When it happens

Trigger: fetch_manifest_bytes or fetch_signed_member calls fetch_url_bytes; the reqwest client sends the GET successfully (no transport error) but response.status().is_success() is false — e.g. 404 because the URL/path in Distro.toml is wrong, 403 from a private repo, 429 rate limit, or 5xx from the host.

Common situations: Typo'd or stale capsule source URLs in Distro.toml; manifest moved to a new tag/branch so the pinned URL 404s; GitHub raw URL pointing at a private repository without a token; corporate proxy or CDN returning 403/502; temporarily down hosting service returning 500.

Understand the failure class

Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.

Related errors


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