{"record":{"id":"8f0df31b22a29c6d","repo":"BoundaryML/baml","slug":"openai-transcription-audio-must-be-resolved-to-base64-before","errorCode":null,"errorMessage":"OpenAI transcription audio must be resolved to base64 before request building","messagePattern":"OpenAI transcription audio must be resolved to base64 before request building","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"engine/baml-runtime/src/internal/llm_client/primitive/openai/types.rs","lineNumber":92,"sourceCode":"    }\n\n    if audio_parts.len() != 1 {\n        bail!(\n            \"OpenAI transcriptions require exactly one audio media part, got {}\",\n            audio_parts.len()\n        );\n    }\n\n    let audio = audio_parts\n        .pop()\n        .expect(\"audio_parts length was already validated\");\n    let mime = audio.mime_type_as_ok()?;\n    let file_bytes = match &audio.content {\n        BamlMediaContent::Base64(media_b64) => BASE64_STANDARD\n            .decode(&media_b64.base64)\n            .context(\"Failed to decode transcription audio as base64\")?,\n        BamlMediaContent::Url(_) | BamlMediaContent::File(_) => {\n            bail!(\"OpenAI transcription audio must be resolved to base64 before request building\")\n        }\n    };\n\n    let mut fields = BamlMap::new();\n    let model = required_string_field(properties, \"model\")?;\n    fields.insert(\"model\".to_string(), model.clone());\n\n    let property_prompt = optional_string_field(properties, \"prompt\")?;\n    if property_prompt.is_some() && !text_parts.is_empty() {\n        bail!(\"OpenAI transcription prompt is ambiguous: both properties.prompt and rendered text were provided\");\n    }\n    let rendered_prompt = (!text_parts.is_empty()).then(|| text_parts.join(\"\\n\"));\n    if let Some(prompt) = property_prompt.or(rendered_prompt) {\n        fields.insert(\"prompt\".to_string(), prompt);\n    }\n\n    if let Some(language) = optional_string_field(properties, \"language\")? {\n        fields.insert(\"language\".to_string(), language);","sourceCodeStart":74,"sourceCodeEnd":110,"githubUrl":"https://github.com/BoundaryML/baml/blob/bd85ce9dee1463ff04d27efd20531013a4ff46c1/engine/baml-runtime/src/internal/llm_client/primitive/openai/types.rs#L74-L110","documentation":"OpenAI's audio transcription endpoint needs raw audio bytes (multipart upload), so BAML requires the media content to already be Base64-resolved before building the request. If the audio still holds a Url or File reference (unresolved media), build_transcription_parts cannot produce bytes and bails.","triggerScenarios":"Passing a BamlMedia whose content is BamlMediaContent::Url or BamlMediaContent::File into a transcription request without prior media resolution (the media was constructed manually or resolution was skipped/bypassed).","commonSituations":"Constructing BamlMedia::Url directly in code and sending it to a transcription client instead of letting the runtime resolve it; using a file-path media in a context where the resolution pass doesn't run (custom clients/low-level API usage).","solutions":["Let the BAML runtime resolve the media (pass the media through the normal function call path) instead of building requests manually.","If using the low-level API, pre-resolve the media to base64 (read the file or fetch the URL and encode) before calling request building.","Convert a File/Url media to BamlMediaContent::Base64 with the correct mime_type yourself."],"exampleFix":"// before\nlet media = BamlMedia::url(\"audio/mpeg\", \"https://example.com/a.mp3\");\n// after\nlet bytes = std::fs::read(\"a.mp3\")?;\nlet media = BamlMedia::base64(\"audio/mpeg\", BASE64_STANDARD.encode(bytes));","handlingStrategy":"validation","validationCode":"// Ensure media is base64-resolved before low-level request building\nif (media.content.kind !== \"base64\") {\n  throw new Error(\"Resolve audio to base64 (pass through the BAML runtime) before building requests\");\n}","typeGuard":"const isBase64Media = (m) => m.content && typeof m.content.base64 === \"string\";","tryCatchPattern":"// Catch and re-resolve\ntry {\n  parts = build_transcription_parts(&props, &prompt)?;\n} catch (e) {\n  if (String(e).includes(\"resolved to base64\")) {\n    media = await resolveMediaToBase64(media); // retry with resolved media\n  } else { throw e; }\n}","preventionTips":["Always create media via the BAML runtime's media helpers (baml.Audio from file/url) so resolution happens automatically.","Never feed raw BamlMedia::url/file values into low-level request-building APIs.","Pre-fetch remote audio and hand BAML base64 with an explicit mime_type."],"tags":["openai","transcription","audio","media","baml"],"backgroundTag":"internal-invariant-violation","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"}