sigoden/aichat · error

The model does not support network images

Error message

The model does not support network images: {:?}

What it means

Thrown by gemini_build_chat_completions_body in src/client/vertexai.rs:384 when a chat completions request includes image URLs that are remote http(s) URLs rather than base64 data URLs. The Gemini/Vertex AI body builder only converts `data:<mime>;base64,<data>` image URLs into inline_data parts; any other URL is collected into network_image_urls and the request is rejected before it is sent. The library does this because the Gemini generateContent endpoint it targets does not accept remote image URLs in message parts.

Solutions

  1. Download the image and inline it as a base64 data URL, e.g. "data:image/jpeg;base64,<base64 bytes>", before building the message.
  2. Use a helper to fetch the URL, detect its MIME type, and base64-encode the bytes into the ImageUrl `url` field.
  3. If the images are stored on GCS, switch to a Gemini file/inline reference supported by the API instead of a public https URL.
  4. Strip or replace image parts with a text description when the target model doesn't support vision.

Example fix

// before
MessageContentPart::ImageUrl { image_url: ImageUrl { url: "https://example.com/cat.jpg".into() } }
// after
let bytes = reqwest::get("https://example.com/cat.jpg").await?.bytes().await?;
let data_url = format!("data:image/jpeg;base64,{}", base64::engine::general_purpose::STANDARD.encode(&bytes));
MessageContentPart::ImageUrl { image_url: ImageUrl { url: data_url } }
Defensive patterns

Strategy: validation

Validate before calling

fn assert_no_network_images(messages: &[Message]) -> Result<(), Vec<String>> {
    let mut bad = vec![];
    for msg in messages {
        if let MessageContent::Array(parts) = &msg.content {
            for p in parts {
                if let MessageContentPart::ImageUrl { image_url: ImageUrl { url } } = p {
                    if !url.starts_with("data:") {
                        bad.push(url.clone());
                    }
                }
            }
        }
    }
    if bad.is_empty() { Ok(()) } else { Err(bad) }
}

Type guard

fn is_inline_image_url(url: &str) -> bool {
    url.starts_with("data:") && url.contains(";base64,")
}

Try / catch

let body = match gemini_build_chat_completions_body(data, &model) {
    Ok(b) => b,
    Err(e) if e.to_string().starts_with("The model does not support network images") => {
        // convert URLs to base64 data URLs and rebuild
        let data = inline_all_images(data).await?;
        gemini_build_chat_completions_body(data, &model)?
    }
    Err(e) => return Err(e.into()),
};

Prevention

When it happens

Trigger: Calling chat_completions on a Vertex AI/Gemini model with a message content part of type ImageUrl whose url is a network URL (e.g. "https://example.com/cat.jpg") instead of a base64 data URL like "data:image/jpeg;base64,...". Every non-data: URL in the request causes this bail, listing all offending URLs.

Common situations: Porting code from OpenAI-style clients (which accept https image URLs) to this Vertex AI client; loading image URLs directly from a database or user input without downloading/encoding them; forgetting the multipart/inline image step in an image-captioning pipeline.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


AI-assisted analysis of sigoden/aichat@82976d349a (2026-09-09). Data as JSON: /api/errors/2e013984b85e094b. Report an issue: GitHub.

Appendix: source

Thrown at src/client/vertexai.rs:384

                                    "name": tool_result.call.name,
                                    "response": {
                                        "name": tool_result.call.name,
                                        "content": tool_result.output,
                                    }
                                }
                            })
                        }).collect();
                        vec![
                            json!({ "role": "model", "parts": model_parts }),
                            json!({ "role": "function", "parts": function_parts }),
                        ]
                    }
                }
        })
        .collect();

    if !network_image_urls.is_empty() {
        bail!(
            "The model does not support network images: {:?}",
            network_image_urls
        );
    }

    let mut body = json!({ "contents": contents, "generationConfig": {} });

    if let Some(v) = system_message {
        body["systemInstruction"] = json!({ "parts": [{"text": v }] });
    }

    if let Some(v) = model.max_tokens_param() {
        body["generationConfig"]["maxOutputTokens"] = v.into();
    }
    if let Some(v) = temperature {
        body["generationConfig"]["temperature"] = v.into();
    }
    if let Some(v) = top_p {

View on GitHub (pinned to 82976d349a)