From cf2af23596d24e13e9a025e95abebb9bc9e8136c Mon Sep 17 00:00:00 2001 From: devteamaegis Date: Tue, 9 Jun 2026 14:40:52 -0400 Subject: [PATCH] fix(media): don't crash when Anthropic/Vertex media block data is bytes The Anthropic ('type':'base64') and Vertex ('type':'media') branches in _find_and_process_media built a data URI via string concatenation with data['data'], assuming it is always a str. When a logged payload carries raw bytes there, this raised an uncaught TypeError that propagated out of span creation. Guard both branches with isinstance(data['data'], str) so non-string media data passes through unprocessed instead of crashing. --- langfuse/_task_manager/media_manager.py | 2 ++ tests/unit/test_media_manager.py | 22 ++++++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/langfuse/_task_manager/media_manager.py b/langfuse/_task_manager/media_manager.py index 7a7123798..57844df20 100644 --- a/langfuse/_task_manager/media_manager.py +++ b/langfuse/_task_manager/media_manager.py @@ -126,6 +126,7 @@ def _process_data_recursively(data: Any, level: int) -> Any: and data["type"] == "base64" and "media_type" in data and "data" in data + and isinstance(data["data"], str) ): media = LangfuseMedia( base64_data_uri=f"data:{data['media_type']};base64," + data["data"], @@ -150,6 +151,7 @@ def _process_data_recursively(data: Any, level: int) -> Any: and data["type"] == "media" and "mime_type" in data and "data" in data + and isinstance(data["data"], str) ): media = LangfuseMedia( base64_data_uri=f"data:{data['mime_type']};base64," + data["data"], diff --git a/tests/unit/test_media_manager.py b/tests/unit/test_media_manager.py index 68684fac4..da289b781 100644 --- a/tests/unit/test_media_manager.py +++ b/tests/unit/test_media_manager.py @@ -194,3 +194,25 @@ def test_find_and_process_media_sse_in_nested_structure_passes_through(): assert result == data assert queue.empty() + + +def test_find_and_process_media_base64_block_with_bytes_data_passes_through(): + queue = Queue() + manager = MediaManager( + api_client=SimpleNamespace(media=Mock()), + httpx_client=Mock(), + media_upload_queue=queue, + ) + + # Anthropic/Vertex media blocks whose "data" is raw bytes rather than a + # base64 string must not crash media processing. + data = [ + {"type": "base64", "media_type": "image/png", "data": b"\x89PNG raw"}, + {"type": "media", "mime_type": "image/png", "data": b"\x89PNG raw"}, + ] + result = manager._find_and_process_media( + data=data, trace_id="trace-id", observation_id=None, field="input" + ) + + assert result == data + assert queue.empty()