BoundaryML/baml · error · anyhow::Error

Non-text part encountered

Error message

Non-text part encountered

What it means

This path combines all message parts by mapping each part to its text, and errors if any part is not ChatMessagePart::Text. It is used where only plain-text messages are valid, so media or structured parts cannot be represented.

Source

Thrown at engine/baml-runtime/src/internal/llm_client/primitive/openai/openai_client.rs:371

                Ok(json!(parts_to_message(&content.parts)?))
            }
            ProviderStrategy::StandardOpenAI { provider } => {
                if provider == "openai-generic" {
                    // Check if all parts are text
                    let all_text = content
                        .parts
                        .iter()
                        .all(|part| matches!(part, ChatMessagePart::Text(_)));
                    if all_text {
                        // Concatenate all text parts into a single string
                        let combined_text = content
                            .parts
                            .iter()
                            .map(|part| {
                                if let ChatMessagePart::Text(text) = part {
                                    Ok(text.clone())
                                } else {
                                    Err(anyhow::anyhow!("Non-text part encountered"))
                                }
                            })
                            .collect::<Result<Vec<String>>>()?
                            .join(" ");

                        Ok(json!(combined_text))
                    } else {
                        // If there are media parts, use the existing structure
                        Ok(json!(parts_to_message(&content.parts)?))
                    }
                } else {
                    // For other providers, use the existing structure
                    Ok(json!(parts_to_message(&content.parts)?))
                }
            }
        }
    }
}

View on GitHub (pinned to bd85ce9dee)

Solutions

  1. Ensure the prompt contains only text parts for this provider strategy
  2. Move media blocks to a provider/endpoint that supports them (chat completions with media)
  3. Strip or conditionally exclude media from the message before rendering

Example fix

// before
message { media { type image url ctx.img } }
// after
message { "describe the scene" } // text-only for this path
Defensive patterns

Strategy: validation

Validate before calling

// ensure all parts are text before this path
const allText = msg.parts.every(p => typeof p === "string" || p.type === "text");
if (!allText) throw new Error("Only text parts allowed for this provider path");

Type guard

function isTextPart(p: unknown): p is { type: "text"; text: string } {
  return typeof p === "object" && p !== null && (p as any).type === "text";
}

Prevention

When it happens

Trigger: format_message_content called on a message whose parts include non-text ChatMessagePart variants (Image, Audio, WithMeta, etc.) while the code path requires text-only parts.

Common situations: Passing an image or audio media block into a provider/endpoint path that only accepts plain text messages (e.g. certain completion formats that concatenate text).

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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