zeroclaw-labs/zeroclaw · error · anyhow::Error

Failed to download Flux image from {image_url}

Error message

Failed to download Flux image from {image_url}

What it means

try_flux's generation request succeeded and returned an image URL, but the follow-up GET that downloads the bytes from /images/0/url came back non-success. The failure is the asset fetch, not generation: typically the signed URL expired, the CDN was unreachable, or an egress path allows the API host but not the image host.

Source

Thrown at crates/zeroclaw-tools/src/linkedin_client.rs:1167

        let json: serde_json::Value = resp.json().await?;
        let image_url = json
            .pointer("/images/0/url")
            .and_then(|v| v.as_str())
            .ok_or_else(|| {
                ::zeroclaw_log::record!(
                    ERROR,
                    ::zeroclaw_log::Event::new(module_path!(), ::zeroclaw_log::Action::Fail)
                        .with_outcome(::zeroclaw_log::EventOutcome::Failure)
                        .with_attrs(::serde_json::json!({"image_provider": "flux"})),
                    "linkedin_client: Flux response missing image URL"
                );
                anyhow::Error::msg("No image URL in Flux response")
            })?;

        // Download the image from the returned URL
        let img_resp = client.get(image_url).send().await?;
        if !img_resp.status().is_success() {
            anyhow::bail!("Failed to download Flux image from {image_url}");
        }
        let bytes = img_resp.bytes().await?;
        let path = output_dir.join(format!("{base_name}_flux.png"));
        tokio::fs::write(&path, &bytes).await?;
        Ok(path)
    }

    // ── SVG Fallback Card ───────────────────────────────────────

    /// Generate a branded SVG text card with the post title on a gradient background.
    pub fn generate_fallback_card(title: &str, accent_color: &str) -> String {
        // Truncate title to ~80 bytes for clean display without splitting UTF-8.
        let display_title = if title.len() > 80 {
            let end = crate::util_helpers::floor_char_boundary(title, 77);
            format!("{}...", &title[..end])
        } else {
            title.to_string()
        };

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Regenerate (call generate again) to obtain a fresh signed URL — do not retry the stale one
  2. Download immediately after generation and never persist the URL for later use
  3. Check proxy/egress rules cover the image host, not just the API endpoint

Example fix

// before: reusing a saved URL
let img = client.get(saved_flux_url).send().await?;

// after: re-generate to get a fresh signed URL
let path = match generator.generate(prompt).await {
    Ok(p) => p,
    Err(e) if e.to_string().contains("Failed to download Flux image") => {
        generator.generate(prompt).await? // fresh URL
    }
    Err(e) => return Err(e),
};
Defensive patterns

Strategy: retry

Try / catch

Catch errors containing 'Failed to download Flux image' and re-run generate() once to obtain a fresh signed URL; do not retry the stale URL — expiry makes it permanently invalid.

Prevention

When it happens

Trigger: Delay between generation and download long enough for the signed URL to expire (4xx); CDN/network failure (5xx); proxy or firewall rules that permit the Flux API domain but block the image host.

Common situations: Saving URLs and downloading them later instead of immediately; corporate proxies with domain allowlists; slow networks causing the download to start after the URL's TTL.

Related errors


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