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()