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

  1. 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
  2. Retry the whole generate call to obtain a fresh signed URL
  3. Tighten the gap between generation and download - avoid slow synchronous work in between
  4. 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

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


AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23). Data as JSON: /api/errors/a1a9016eedc1ae7f. Report an issue: GitHub.