{"record":{"id":"dfd72c31c19637f5","repo":"Hmbown/CodeWhale","slug":"stored-image-requires-canonical-inline-content","errorCode":null,"errorMessage":"stored image requires canonical inline content","messagePattern":"stored image requires canonical inline content","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/image_attach.rs","lineNumber":179,"sourceCode":"                bail!(\"image {} base64 is not canonical\", index + 1);\n            }\n            Ok(attached.content_block())\n        })\n        .collect()\n}\n\n/// Reuse durable canonical bytes for retry, never reread a path or URL.\npub(crate) fn runtime_images_from_blocks(\n    blocks: &[ContentBlock],\n) -> Result<Vec<RuntimeImageInput>> {\n    let mut images = Vec::new();\n    for block in blocks {\n        if let ContentBlock::ImageUrl { image_url } = block {\n            if image_url.url.len() > MAX_IMAGE_BYTES.div_ceil(3) * 4 + 32 {\n                bail!(\"stored image exceeds the attachment limit\");\n            }\n            let (mime, data) = parse_data_url(&image_url.url)\n                .ok_or_else(|| anyhow::anyhow!(\"stored image requires canonical inline content\"))?;\n            images.push(RuntimeImageInput {\n                mime: mime.to_string(),\n                data_base64: data.to_string(),\n            });\n        }\n    }\n    prepare_stored_images(&images)?;\n    Ok(images)\n}\n\n/// Validate new image-bearing durable records without rewriting their block order.\n/// Legacy schema 2 history continues to use its original interpretation.\npub(crate) fn validate_stored_image_content(blocks: &[ContentBlock]) -> Result<()> {\n    if blocks.iter().any(|block| {\n        !matches!(\n            block,\n            ContentBlock::Text { .. } | ContentBlock::ImageUrl { .. }\n        )","sourceCodeStart":161,"sourceCodeEnd":197,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/image_attach.rs#L161-L197","documentation":"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).","triggerScenarios":"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.","commonSituations":"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.","solutions":["Convert the image to inline canonical form: fetch the bytes, base64-encode them, and store `data:<mime>;base64,<payload>`.","Check that the stored URL is a data URL with an explicit mime type and `;base64,` marker, not a plain http link.","Remove the block from history if the image content is no longer needed for the conversation."],"exampleFix":"// before\n{ \"type\": \"image_url\", \"image_url\": { \"url\": \"https://example.com/img.png\" } }\n\n// after\n{ \"type\": \"image_url\", \"image_url\": { \"url\": \"data:image/png;base64,iVBORw0KGgo...\" } }","handlingStrategy":"validation","validationCode":"fn is_canonical_inline_data_url(url: &str) -> bool {\n    let Some(rest) = url.strip_prefix(\"data:\") else { return false };\n    let Some((mime, payload)) = rest.split_once(\";base64,\") else { return false };\n    !mime.is_empty() && !payload.is_empty()\n}","typeGuard":null,"tryCatchPattern":"// Rust\nfor block in blocks {\n    if let ContentBlock::ImageUrl { image_url } = block {\n        if !image_url.url.starts_with(\"data:\") {\n            // remote URL: fetch + inline before admitting\n            image_url.url = inline_as_data_url(&fetch(image_url.url)?);\n        }\n    }\n}","preventionTips":["Never persist remote http(s) image URLs in stored history; always inline as data URLs.","Run validate_stored_image_content on session logs after config or provider migrations.","Generate data URLs with an explicit mime type and `;base64,` marker."],"tags":["image","data-url","validation"],"backgroundTag":"invalid-url-format","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}