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
- Re-fetch the artifact metadata to mint a fresh signed URL and retry the download
- Verify the artifact_uid still exists server-side before retrying
- Start the download immediately after obtaining the URL instead of queueing it
- 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
- Download immediately after minting the signed URL; never persist URLs across sessions
- Keep the 300s transport timeout but retry once on 4xx/5xx with a newly minted URL
- Bypass proxies that rewrite query strings on signed URLs
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
- invalid value 'integration'
- invalid value 'schedule'
- invalid value 'secret'
- invalid value 'harness-support'
- invalid value 'artifact'
AI-assisted analysis of warpdotdev/warp@e72fd7aacb (2026-08-16).
Data as JSON: /api/errors/8a2b795d4ac82e32.
Report an issue: GitHub.