{"record":{"id":"9929953d6467aae7","repo":"BoundaryML/baml","slug":"openai-transcriptions-require-chat-messages-with-exactly-one","errorCode":null,"errorMessage":"OpenAI transcriptions require chat messages with exactly one audio media part","messagePattern":"OpenAI transcriptions require chat messages with exactly one audio media part","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"engine/baml-runtime/src/internal/llm_client/primitive/openai/types.rs","lineNumber":62,"sourceCode":"    #[serde(rename = \"transcript.text.done\")]\n    Done {\n        text: String,\n        usage: Option<TranscriptionUsage>,\n    },\n    #[serde(rename = \"transcript.text.segment\")]\n    Segment,\n    #[serde(other)]\n    Unknown,\n}\n\npub fn build_transcription_parts(\n    properties: &BamlMap<String, Value>,\n    prompt: either::Either<&String, &[RenderedChatMessage]>,\n) -> Result<TranscriptionParts> {\n    let messages = match prompt {\n        either::Either::Right(messages) => messages,\n        either::Either::Left(_) => {\n            bail!(\"OpenAI transcriptions require chat messages with exactly one audio media part\")\n        }\n    };\n\n    reject_reserved_request_fields(properties)?;\n\n    let mut audio_parts = Vec::new();\n    let mut text_parts = Vec::new();\n    for message in messages {\n        for part in &message.parts {\n            collect_transcription_prompt_parts(part, &mut audio_parts, &mut text_parts)?;\n        }\n    }\n\n    if audio_parts.len() != 1 {\n        bail!(\n            \"OpenAI transcriptions require exactly one audio media part, got {}\",\n            audio_parts.len()\n        );","sourceCodeStart":44,"sourceCodeEnd":80,"githubUrl":"https://github.com/BoundaryML/baml/blob/bd85ce9dee1463ff04d27efd20531013a4ff46c1/engine/baml-runtime/src/internal/llm_client/primitive/openai/types.rs#L44-L80","documentation":"BAML's OpenAI transcription request builder only accepts a chat-style prompt (a list of RenderedChatMessage) containing the audio media; a bare string prompt (Either::Left) has no place to attach audio, so build_transcription_parts rejects it immediately. The error message is slightly misleading — the real requirement is a chat message list, not 'exactly one' at this stage.","triggerScenarios":"Calling a BAML function whose client is configured for OpenAI transcriptions while the prompt is a plain string (no chat message structure), i.e. the function renders to a raw string instead of chat messages.","commonSituations":"Writing a BAML function with a plain `string` prompt and pointing it at a transcription-style client; passing a template that renders to text rather than a message[]; older clients/tests that predate the chat-message transcription format.","solutions":["Rewrite the BAML function to produce chat messages (e.g. use a message-based prompt with an audio media part) instead of a bare string prompt.","Ensure the audio file is passed as a media part in one of the chat messages.","If you only need text-to-text, do not route the function through a transcription client."],"exampleFix":"// before\nfunction Transcribe(audio: Audio) -> string {\n  client openai/transcribe\n  prompt #\"transcribe\"# // plain string prompt\n}\n// after\nfunction Transcribe(audio: Audio) -> string {\n  client openai/transcribe\n  prompt #\"\n    {{ ctx.output_format }}\n    {{ _.role('user') }}\n    {{ audio }}\n  \"#\n}","handlingStrategy":"validation","validationCode":"// Ensure the function prompt renders as chat messages containing an audio part\nfunction assertChatAudioPrompt(fn) {\n  const rendered = baml.renderPrompt(fn);\n  if (typeof rendered === \"string\") {\n    throw new Error(\"Transcription functions must use a chat-message prompt, not a plain string\");\n  }\n}","typeGuard":"const isChatMessages = (p) =>\n  Array.isArray(p) && p.some(m => m.parts?.some(pt => pt.type === \"audio\"));","tryCatchPattern":null,"preventionTips":["Always write transcription function prompts as message-based templates with {{ audio }}.","Keep transcription functions separate from plain string-prompt functions.","Test each transcription function with a sample audio input before deploying."],"tags":["openai","transcription","audio","baml"],"backgroundTag":"invalid-argument-value","analyzedSha":"bd85ce9dee1463ff04d27efd20531013a4ff46c1","analyzedAt":"2026-09-12T03:38:25.718Z","contentChangedAt":"2026-09-12T03:38:25.718Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}