Hmbown/CodeWhale · error
image MIME does not match its content
Error message
image {} MIME does not match its content What it means
The image's declared MIME type does not match the media type detected from its actual bytes by encode_image_bytes (which sniffs content, e.g. via magic bytes). prepare_images_with_limit rejects the mismatch so the provider-neutral history never carries a mislabeled content block, since downstream consumers trust the declared media_type.
Solutions
- Derive the MIME type from the image bytes (magic-number sniffing) instead of the filename or client header.
- Fix the stored record's mime field to match the real content format.
- Re-encode the image into the format its declared MIME claims if that format is required.
- At ingestion, reject or normalize mismatched MIME before the image reaches this validation path.
Example fix
// before
RuntimeImageInput { mime: "image/png", data_base64: jpeg_b64, .. }
// after
RuntimeImageInput { mime: "image/jpeg", data_base64: jpeg_b64, .. } Defensive patterns
Strategy: validation
Validate before calling
fn mime_matches(data_base64: &str, claimed: &str) -> bool {
STANDARD.decode(data_base64)
.ok()
.and_then(|b| infer_image_mime(&b))
.map_or(false, |detected| detected == claimed)
} Type guard
fn sniffed_mime(bytes: &[u8]) -> Option<&'static str> {
match bytes.first()? {
0x89 if bytes.start_with(b"\x89PNG") => Some("image/png"),
0xFF if bytes.start_with(&[0xFF, 0xD8]) => Some("image/jpeg"),
b'G' if bytes.start_with(b"GIF8") => Some("image/gif"),
_ => None,
}
} Try / catch
match prepare_runtime_images(&images) {
Err(e) if e.to_string().contains("MIME does not match") => {
eprintln!("derive mime from magic bytes, not the filename"); }
other => other?,
} Prevention
- Never trust filename extension or client Content-Type for image MIME
- Sniff magic bytes at ingestion and set mime from the result
- Add a fixture test that attaches a mislabeled JPEG as PNG
When it happens
Trigger: prepare_runtime_images or prepare_stored_images receives a RuntimeImageInput whose mime field says e.g. image/png but whose bytes are actually JPEG/GIF/WebP (or vice versa) — detected when image.mime != attached.media_type after content sniffing.
Common situations: Renaming a .jpg to .png and setting mime from the extension; copy-paste code that hardcodes "image/png"; HTTP uploads where Content-Type came from the browser's guess; stored records written by an older path that trusted client-supplied MIME.
Understand the failure class
Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.
Related errors
- image has invalid base64
- images exceed the 5 MiB total limit
- images exceed the attachment limit
- invalid persisted user image content kind
- invalid tool image evidence
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/a89fb8188fdfb78d.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/image_attach.rs:156
);
}
let bytes = STANDARD
.decode(&image.data_base64)
.map_err(|_| anyhow::anyhow!("image {} has invalid base64", index + 1))?;
if bytes.len() > per_image_limit {
bail!(
"image {} exceeds the {} MiB limit",
index + 1,
per_image_limit / (1024 * 1024)
);
}
total = total.saturating_add(bytes.len());
if total_limit.is_some_and(|limit| total > limit) {
bail!("images exceed the 5 MiB total limit");
}
let attached = encode_image_bytes(&bytes, &format!("image {}", index + 1))?;
if image.mime != attached.media_type {
bail!("image {} MIME does not match its content", index + 1);
}
decode_and_guard_image(&bytes)?;
// Standard padded base64 is the one replay representation.
if STANDARD.encode(&bytes) != image.data_base64 {
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 {View on GitHub (pinned to 73e0f67d83)