warpdotdev/warp · error

Artifact download failed: {err}

Error message

Artifact download failed: {err}

What it means

The artifact download GET to artifact.download_url() succeeded at the transport layer but error_for_status() saw a non-success HTTP status; the wrapped status error is surfaced with this message. Signed artifact URLs are short-lived, so a 403 from an expired signature is the canonical cause.

Source

Thrown at app/src/ai/artifact_download.rs:92

        tokio::fs::create_dir_all(parent).await.with_context(|| {
            format!("Failed to create download directory '{}'", parent.display())
        })?;
    }

    let response = http_client
        .get(artifact.download_url())
        .timeout(Duration::from_secs(300))
        .send()
        .await
        .with_context(|| {
            format!(
                "Failed to download artifact '{}' from signed URL",
                artifact.artifact_uid()
            )
        })?;
    let response = response
        .error_for_status()
        .map_err(|err| anyhow!("Artifact download failed: {err}"))?;

    let mut file = tokio::fs::File::create(path)
        .await
        .with_context(|| format!("Failed to create download file '{}'", path.display()))?;
    let mut response_stream =
        StreamReader::new(response.bytes_stream().map_err(std::io::Error::other));
    tokio::io::copy(&mut response_stream, &mut file)
        .await
        .with_context(|| format!("Failed to write download file '{}'", path.display()))?;
    file.sync_data()
        .await
        .with_context(|| format!("Failed to sync download file '{}'", path.display()))?;

    Ok(())
}

#[cfg(test)]
#[path = "artifact_download_tests.rs"]

View on GitHub (pinned to e72fd7aacb)

Solutions

  1. Re-fetch the artifact metadata to mint a fresh signed URL and retry the download
  2. Verify the artifact_uid still exists server-side before retrying
  3. Start the download immediately after obtaining the URL instead of queueing it
  4. Check corporate proxies/VPCs that rewrite or cache signed URLs

Example fix

// before
let response = client.get(url.clone()).send().await?;
let response = response.error_for_status().map_err(|err| anyhow!("Artifact download failed: {err}"))?;

// after - refresh the signed URL once on status failure
let mut response = client.get(url.clone()).send().await?;
if !response.status().is_success() {
    let fresh = artifact_api.refetch_download_url(artifact.artifact_uid()).await?;
    response = client.get(fresh).send().await?;
}
let response = response.error_for_status().map_err(|err| anyhow!("Artifact download failed: {err}"))?;
Defensive patterns

Strategy: retry

Validate before calling

if let Some(expiry) = url_expires_at(&url) {
    if expiry - Instant::now() < Duration::from_secs(30) {
        let url = artifact_api.refetch_download_url(uid).await?; // refresh before use
    }
}

Try / catch

let resp = match download(artifact).await {
    Err(e) if e.to_string().starts_with("Artifact download failed") => {
        let fresh = refetch_url(artifact.artifact_uid()).await?;
        download_with_url(fresh).await? // one bounded retry with a fresh signed URL
    }
    r => r?,
};

Prevention

When it happens

Trigger: reqwest GET on the signed URL returns 4xx/5xx: 403 expired/mismatched signature, 404 deleted or nonexistent artifact_uid, 5xx from the object store / CDN (artifact_download.rs:87-92).

Common situations: Delay between obtaining the signed URL and starting the download exceeds the URL TTL; URL fetched through a rewriting proxy that strips query params; artifact deleted before download; transient CDN errors.

Related errors


AI-assisted analysis of warpdotdev/warp@e72fd7aacb (2026-08-16). Data as JSON: /api/errors/8a2b795d4ac82e32. Report an issue: GitHub.