BoundaryML/baml · error · minijinja::Error

CannotUnpack

CannotUnpack

Error message

Media variable had unrecognizable data: {media_data}

What it means

BAML passes media (images/audio/etc.) through chat templates using special `:baml-media:`-prefixed, `:baml-end-media:`-suffixed string markers. When splitting message parts, each media marker's payload must parse as a BamlMedia JSON object; if serde_json::from_str::<BamlMedia> fails, the raw payload cannot be interpreted as media and this CannotUnpack error is thrown.

Source

Thrown at engine/baml-lib/jinja-runtime/src/lib.rs:443

                }
            }
        } else if role.is_none() && chunk.is_empty() {
            // If there's only whitespace before the first `_.chat()` directive, we discard that chunk
        } else {
            let mut parts = vec![];
            for part in chunk.split(MAGIC_MEDIA_DELIMITER) {
                let part = if part.starts_with(":baml-start-media:")
                    && part.ends_with(":baml-end-media:")
                {
                    let media_data = part
                        .strip_prefix(":baml-start-media:")
                        .unwrap_or(part)
                        .strip_suffix(":baml-end-media:")
                        .unwrap_or(part);

                    match serde_json::from_str::<BamlMedia>(media_data) {
                        Ok(m) => Some(ChatMessagePart::Media(m)),
                        Err(_) => Err(minijinja::Error::new(
                            ErrorKind::CannotUnpack,
                            format!("Media variable had unrecognizable data: {media_data}"),
                        ))?,
                    }
                } else if !part.trim().is_empty() {
                    Some(ChatMessagePart::Text(part.trim().to_string()))
                } else {
                    None
                };

                if let Some(part) = part {
                    if let Some(meta) = &meta {
                        parts.push(part.with_meta(meta.clone()));
                    } else {
                        parts.push(part);
                    }
                }
            }

View on GitHub (pinned to bd85ce9dee)

Solutions

  1. Pass media variables to the template unmodified — never apply string filters or transformations to image/audio parameters.
  2. Verify the value is an actual BAML media object (created via image(...)/audio(...) helpers) rather than an arbitrary object.
  3. Print/log the media_data in the error to check for truncation or mangling; fix whatever altered the string.
  4. Align BAML client and runtime versions if the marker format changed between releases.

Example fix

// before
{{ image_var | upper }}

// after
{{ image_var }}
Defensive patterns

Strategy: type-guard

Validate before calling

// ensure the value is an unmodified BAML media object before templating
if (typeof mediaVar === "string" && !mediaVar.includes(":baml-media:")) throw new Error("expected raw BAML media variable");

Type guard

function isBamlMedia(v) { return v && typeof v === "object" && typeof v.kind === "string"; }

Try / catch

match serde_json::from_str::<BamlMedia>(media_data) { Ok(m) => ..., Err(e) => log::error!("media marker corrupt: {media_data}") }

Prevention

When it happens

Trigger: A media variable's marker data is corrupted or truncated before rendering — e.g. the value was string-transformed (trimmed, escaped, re-serialized) between BAML inserting the marker and minijinja rendering it, or an object that isn't a BamlMedia was embedded with the media marker.

Common situations: Applying string filters (upper, trim, replace) to an image variable, which mangles the marker JSON; passing a non-media value where an image is expected; BAML version mismatches changing the internal marker encoding.

Understand the failure class

Background: "failed to unmarshal" / json.Unmarshal errors: why parsing a response into a Go struct fails and how to fix it — this error's family across 23 libraries.

Related errors


AI-assisted analysis of BoundaryML/baml@bd85ce9dee (2026-09-12). Data as JSON: /api/errors/929af450da120483. Report an issue: GitHub.