Hmbown/CodeWhale · error

images exceed the attachment limit

Error message

images exceed the {MAX_RUNTIME_IMAGES} attachment limit

What it means

The runtime image attach path enforces MAX_RUNTIME_IMAGES images per call; prepare_runtime_images bails before decoding when more are supplied. This is a hard count limit on inline images admitted into a single request's content blocks.

Solutions

  1. Split the request: send images across multiple turns/requests, keeping each batch within MAX_RUNTIME_IMAGES.
  2. Pre-select the most relevant images and drop the rest before calling the API.
  3. Raise MAX_RUNTIME_IMAGES only deliberately, since the limit protects the model context and payload size.
  4. In UI code, disable further attachments once the limit is reached and show a counter.

Example fix

// before
blocks = prepare_runtime_images(&all_30_images)?;
// after
for batch in all_30_images.chunks(MAX_RUNTIME_IMAGES) {
    blocks.extend(prepare_runtime_images(batch)?);
}
Defensive patterns

Strategy: validation

Validate before calling

if images.len() > MAX_RUNTIME_IMAGES {
    return Err(anyhow!("too many images: {} > {}", images.len(), MAX_RUNTIME_IMAGES));
}

Try / catch

match prepare_runtime_images(&images) {
    Err(e) if e.to_string().contains("attachment limit") => {
        eprintln!("send at most {} images per request", MAX_RUNTIME_IMAGES); }
    other => other?,
}

Prevention

When it happens

Trigger: Calling prepare_runtime_images with a slice of RuntimeImageInput whose length exceeds MAX_RUNTIME_IMAGES (e.g. pasting dozens of screenshots into one turn, or batching all images from a directory into one call).

Common situations: A user drags a whole screenshot folder into a session; an automation attaches every chart from a report; a loop that accumulates images without a cap.

Understand the failure class

Background: payload too large / request exceeds maximum size: why libraries cap bytes and how to fix oversize payloads — this error's family across 50 libraries.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/0bfcbdbd14d709b6. Report an issue: GitHub.

Appendix: source

Thrown at crates/tui/src/image_attach.rs:106

    if u64::from(width) * u64::from(height) > MAX_IMAGE_PIXELS
        || width > MAX_IMAGE_DIMENSION
        || height > MAX_IMAGE_DIMENSION
    {
        bail!("image dimensions exceed the decompression bomb guard; downscale or crop first");
    }
    let mut reader = ImageReader::new(Cursor::new(bytes)).with_guessed_format()?;
    reader.limits(limits());
    let decoded = reader
        .decode()
        .map_err(|_| anyhow::anyhow!("invalid image content or decode allocation limit"))?;
    Ok((decoded, width, height))
}

/// Validate untrusted inline input before route selection or durable admission.
/// Return the existing provider-neutral history representation; no file is opened.
pub(crate) fn prepare_runtime_images(images: &[RuntimeImageInput]) -> Result<Vec<ContentBlock>> {
    if images.len() > MAX_RUNTIME_IMAGES {
        bail!("images exceed the {MAX_RUNTIME_IMAGES} attachment limit");
    }
    prepare_images_with_limit(
        images,
        MAX_RUNTIME_IMAGE_BYTES,
        Some(MAX_RUNTIME_IMAGE_TOTAL_BYTES),
    )
}

/// Internal Engine/history input retains the established local 5 MiB ceiling.
/// Network callers must first pass `prepare_runtime_images` (4 MiB per image,
/// 10 images and 5 MiB total). Local history never had those aggregate/count
/// limits; impose only its existing per-image bound and bounded full decode.
pub(crate) fn prepare_stored_images(images: &[RuntimeImageInput]) -> Result<Vec<ContentBlock>> {
    prepare_images_with_limit(images, MAX_IMAGE_BYTES, None)
}

fn prepare_images_with_limit(
    images: &[RuntimeImageInput],

View on GitHub (pinned to 73e0f67d83)