diff --git a/langfuse/langchain/CallbackHandler.py b/langfuse/langchain/CallbackHandler.py index dac76f4fc..bdc5bace6 100644 --- a/langfuse/langchain/CallbackHandler.py +++ b/langfuse/langchain/CallbackHandler.py @@ -343,7 +343,7 @@ def _get_error_level_and_status_message( if any(isinstance(error, t) for t in CONTROL_FLOW_EXCEPTION_TYPES): return "DEFAULT", str(error) or type(error).__name__ - return "ERROR", str(error) + return "ERROR", str(error) or type(error).__name__ def _get_observation_type_from_serialized( self, serialized: Optional[Dict[str, Any]], callback_type: str, **kwargs: Any diff --git a/tests/unit/test_langchain.py b/tests/unit/test_langchain.py index c55eaa3d2..95c81d547 100644 --- a/tests/unit/test_langchain.py +++ b/tests/unit/test_langchain.py @@ -487,6 +487,32 @@ class DummyControlFlowError(RuntimeError): ) +def test_error_without_message_uses_exception_type_as_status_message( + langfuse_memory_client, get_span +): + handler = CallbackHandler() + run_id = uuid4() + + handler.on_tool_start( + {"name": "failing_tool"}, + "{}", + run_id=run_id, + ) + handler.on_tool_error( + ValueError(), + run_id=run_id, + ) + + handler._langfuse_client.flush() + + span = get_span("failing_tool") + assert span.attributes[LangfuseOtelSpanAttributes.OBSERVATION_LEVEL] == "ERROR" + assert ( + span.attributes[LangfuseOtelSpanAttributes.OBSERVATION_STATUS_MESSAGE] + == "ValueError" + ) + + def test_streamed_llm_error_cleans_only_its_completion_start_time_memo( langfuse_memory_client, ):