Hmbown/CodeWhale · error

stored image requires canonical inline content

Error message

stored image requires canonical inline content

What it means

A stored ContentBlock::ImageUrl does not carry a parseable canonical inline data URL. parse_data_url returned None, so the block is either a remote http(s) URL, or a data URL that is malformed (missing the `data:` prefix, missing `;base64,`, or non-base64 media).

Solutions

  1. Convert the image to inline canonical form: fetch the bytes, base64-encode them, and store `data:<mime>;base64,<payload>`.
  2. Check that the stored URL is a data URL with an explicit mime type and `;base64,` marker, not a plain http link.
  3. Remove the block from history if the image content is no longer needed for the conversation.

Example fix

// before
{ "type": "image_url", "image_url": { "url": "https://example.com/img.png" } }

// after
{ "type": "image_url", "image_url": { "url": "data:image/png;base64,iVBORw0KGgo..." } }
Defensive patterns

Strategy: validation

Validate before calling

fn is_canonical_inline_data_url(url: &str) -> bool {
    let Some(rest) = url.strip_prefix("data:") else { return false };
    let Some((mime, payload)) = rest.split_once(";base64,") else { return false };
    !mime.is_empty() && !payload.is_empty()
}

Try / catch

// Rust
for block in blocks {
    if let ContentBlock::ImageUrl { image_url } = block {
        if !image_url.url.starts_with("data:") {
            // remote URL: fetch + inline before admitting
            image_url.url = inline_as_data_url(&fetch(image_url.url)?);
        }
    }
}

Prevention

When it happens

Trigger: runtime_images_from_blocks (called by validate_stored_image_content) encounters an ImageUrl block whose image_url.url is not a well-formed `data:<mime>;base64,<payload>` URL — typically an http(s) link or an empty/mangled data URL.

Common situations: Older or third-party session logs that stored remote image URLs instead of inline data; hand-edited history files; providers that emit image URLs rather than base64 content blocks.

Understand the failure class

Background: "Invalid URL" errors: why new URL(), URI.parse, and reqwest::Url reject your string — missing scheme, whitespace, and bad path format — this error's family across 39 libraries.

Related errors


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

Appendix: source

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

                bail!("image {} base64 is not canonical", index + 1);
            }
            Ok(attached.content_block())
        })
        .collect()
}

/// Reuse durable canonical bytes for retry, never reread a path or URL.
pub(crate) fn runtime_images_from_blocks(
    blocks: &[ContentBlock],
) -> Result<Vec<RuntimeImageInput>> {
    let mut images = Vec::new();
    for block in blocks {
        if let ContentBlock::ImageUrl { image_url } = block {
            if image_url.url.len() > MAX_IMAGE_BYTES.div_ceil(3) * 4 + 32 {
                bail!("stored image exceeds the attachment limit");
            }
            let (mime, data) = parse_data_url(&image_url.url)
                .ok_or_else(|| anyhow::anyhow!("stored image requires canonical inline content"))?;
            images.push(RuntimeImageInput {
                mime: mime.to_string(),
                data_base64: data.to_string(),
            });
        }
    }
    prepare_stored_images(&images)?;
    Ok(images)
}

/// Validate new image-bearing durable records without rewriting their block order.
/// Legacy schema 2 history continues to use its original interpretation.
pub(crate) fn validate_stored_image_content(blocks: &[ContentBlock]) -> Result<()> {
    if blocks.iter().any(|block| {
        !matches!(
            block,
            ContentBlock::Text { .. } | ContentBlock::ImageUrl { .. }
        )

View on GitHub (pinned to 73e0f67d83)