zeroclaw-labs/zeroclaw · error
Generated image download failed with HTTP {}
Error message
Generated image download failed with HTTP {} What it means
Bailed when the final (non-redirect) response of the generated-image download is not 2xx (crates/zeroclaw-tools/src/image_gen.rs:283-287). The reqwest client has auto-redirects disabled and a 120s timeout, so this is the last hop answering with a hard failure - typically 403 from an expired signed storage URL, 404 after the artifact was purged, 429 rate limiting, or a 5xx from the storage backend. The concrete HTTP status is embedded in the message.
Source
Thrown at crates/zeroclaw-tools/src/image_gen.rs:284
.await
.context("Failed to download generated image")?;
if response.status().is_redirection() {
if redirect_count == MAX_IMAGE_REDIRECTS {
anyhow::bail!("Too many generated image redirects (max {MAX_IMAGE_REDIRECTS})");
}
let location = response
.headers()
.get(LOCATION)
.ok_or_else(|| anyhow::Error::msg("Generated image redirect omitted Location"))?
.to_str()
.context("Generated image redirect Location is not valid text")?;
current_url = resolve_redirect_url(&target.url, location)?;
continue;
}
if !response.status().is_success() {
anyhow::bail!(
"Generated image download failed with HTTP {}",
response.status()
);
}
return read_generated_image_body(response).await;
}
unreachable!("redirect loop exits through success or redirect limit")
}
/// Read an API key from the environment.
fn read_api_key(env_var: &str) -> Result<String, String> {
std::env::var(env_var)
.map(|v| v.trim().to_string())
.ok()
.filter(|v| !v.is_empty())
.ok_or_else(|| format!("Missing API key: set the {env_var} environment variable"))View on GitHub (pinned to 88bb9c8533)
Solutions
- Read the HTTP status in the message and branch: 403/404 means the URL is dead, regenerate; 429 means back off and retry; 5xx means retry shortly
- Retry the whole generate call to obtain a fresh signed URL
- Tighten the gap between generation and download - avoid slow synchronous work in between
- If 403 persists, review the security.nat64_prefixes configuration so downloads originate from an address the CDN accepts
Example fix
// before: single shot
let out = image_gen.execute(args).await?;
// after: retry once on download HTTP failure (fresh signed URL)
let out = match image_gen.execute(args.clone()).await {
Ok(out) => out,
Err(e) if e.to_string()
.starts_with("Generated image download failed with HTTP") =>
image_gen.execute(args).await?,
Err(e) => return Err(e),
}; Defensive patterns
Strategy: retry
Type guard
fn is_image_download_http_failure(err: &anyhow::Error) -> bool {
err.to_string()
.starts_with("Generated image download failed with HTTP")
} Try / catch
match image_gen.execute(args).await {
Ok(result) => result,
Err(e) if is_image_download_http_failure(&e) => {
let msg = e.to_string();
if msg.contains("HTTP 429") || msg.contains("HTTP 5") {
backoff_then_retry_once(e) // transient: rate limit or server error
} else {
regenerate_image(e) // 403/404: signed URL is dead
}
}
Err(e) => return Err(e),
} Prevention
- Regenerate instead of reusing old image URLs - signed links are short-lived
- Rate-limit concurrent image downloads from a single egress IP
- Keep security.nat64_prefixes aligned with source addresses the CDN accepts
- Treat 429/5xx as retryable and 403/404 as regenerate
When it happens
Trigger: Generation succeeds but the download GET returns >= 400: the fal.ai signed storage link expired before the GET landed (slow NAT64 resolution, slow disk write, or queued download), the object was already deleted, the CDN rate-limited the egress IP, or the origin rejected the NAT64-mapped source address with 403.
Common situations: Large images where time passes between generation and download; shared egress IPs hitting CDN 429 limits; misconfigured security.nat64_prefixes routing downloads through addresses the CDN blocks; brief fal.ai storage incidents returning 5xx.
Related errors
- GLM API error: {error}
- Too many generated image redirects (max {MAX_IMAGE_REDIRECTS
- Jira get_ticket failed ({status}): {}
- Jira search_tickets failed ({status}): {}
- Jira comment_ticket failed ({status}): {}
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/a1a9016eedc1ae7f.
Report an issue: GitHub.