@@ -789,6 +789,10 @@ def _handle_failure(self, kwargs, response_obj, start_time, end_time):
789789 )
790790 span .set_status (Status (StatusCode .ERROR ))
791791 self .set_attributes (span , kwargs , response_obj )
792+
793+ # Record exception information using OTEL standard method
794+ self ._record_exception_on_span (span = span , kwargs = kwargs )
795+
792796 span .end (end_time = self ._to_ns (end_time ))
793797
794798 # Create span for guardrail information
@@ -797,6 +801,87 @@ def _handle_failure(self, kwargs, response_obj, start_time, end_time):
797801 if parent_otel_span is not None :
798802 parent_otel_span .end (end_time = self ._to_ns (datetime .now ()))
799803
804+ def _record_exception_on_span (self , span : Span , kwargs : dict ):
805+ """
806+ Record exception information on the span using OTEL standard methods.
807+
808+ This extracts error information from StandardLoggingPayload and:
809+ 1. Uses span.record_exception() for the actual exception object (OTEL standard)
810+ 2. Sets structured error attributes from StandardLoggingPayloadErrorInformation
811+ """
812+ try :
813+ from litellm .integrations ._types .open_inference import ErrorAttributes
814+
815+ # Get the exception object if available
816+ exception = kwargs .get ("exception" )
817+
818+ # Record the exception using OTEL's standard method
819+ if exception is not None :
820+ span .record_exception (exception )
821+
822+ # Get StandardLoggingPayload for structured error information
823+ standard_logging_payload : Optional [StandardLoggingPayload ] = kwargs .get (
824+ "standard_logging_object"
825+ )
826+
827+ if standard_logging_payload is None :
828+ return
829+
830+ # Extract error_information from StandardLoggingPayload
831+ error_information = standard_logging_payload .get ("error_information" )
832+
833+ if error_information is None :
834+ # Fallback to error_str if error_information is not available
835+ error_str = standard_logging_payload .get ("error_str" )
836+ if error_str :
837+ self .safe_set_attribute (
838+ span = span ,
839+ key = ErrorAttributes .ERROR_MESSAGE ,
840+ value = error_str ,
841+ )
842+ return
843+
844+ # Set structured error attributes from StandardLoggingPayloadErrorInformation
845+ if error_information .get ("error_code" ):
846+ self .safe_set_attribute (
847+ span = span ,
848+ key = ErrorAttributes .ERROR_CODE ,
849+ value = error_information ["error_code" ],
850+ )
851+
852+ if error_information .get ("error_class" ):
853+ self .safe_set_attribute (
854+ span = span ,
855+ key = ErrorAttributes .ERROR_TYPE ,
856+ value = error_information ["error_class" ],
857+ )
858+
859+ if error_information .get ("error_message" ):
860+ self .safe_set_attribute (
861+ span = span ,
862+ key = ErrorAttributes .ERROR_MESSAGE ,
863+ value = error_information ["error_message" ],
864+ )
865+
866+ if error_information .get ("llm_provider" ):
867+ self .safe_set_attribute (
868+ span = span ,
869+ key = ErrorAttributes .ERROR_LLM_PROVIDER ,
870+ value = error_information ["llm_provider" ],
871+ )
872+
873+ if error_information .get ("traceback" ):
874+ self .safe_set_attribute (
875+ span = span ,
876+ key = ErrorAttributes .ERROR_STACK_TRACE ,
877+ value = error_information ["traceback" ],
878+ )
879+
880+ except Exception as e :
881+ verbose_logger .exception (
882+ "OpenTelemetry: Error recording exception on span: %s" , str (e )
883+ )
884+
800885 def set_tools_attributes (self , span : Span , tools ):
801886 import json
802887
0 commit comments