- Package Name: azure-ai-projects
- Package Version: 2.0.1
- Operating System: Ubuntu 22.04 / macOS 15
- Python Version: 3.12
Describe the bug
_ResponsesInstrumentorPreview in azure.ai.projects.telemetry._responses_instrumentor checks span.span_instance.is_recording without parentheses at 15 call sites. Since is_recording is a method on OpenTelemetry spans (not a property), the expression evaluates to a bound method object which is always truthy — the guard never fires.
When the underlying span is a NonRecordingSpan (returned by azure-core-tracing-opentelemetry when tracing is disabled or the span context is invalid), downstream code accesses .attributes which only exists on SDK ReadableSpan, crashing every LLM call with:
AttributeError: 'NonRecordingSpan' object has no attribute 'attributes'
The bug is present at these 15 sites in _responses_instrumentor.py:
| Line |
Context |
| 526 |
_set_span_attribute_safe |
| 849 |
workflow action events |
| 1152 |
tool call events |
| 1641 |
span attribute setting |
| 2617, 2724, 2767, 2794 |
streaming handler (class 1) |
| 3095, 3202, 3244, 3271 |
streaming handler (class 2) |
| 3410, 3608, 3627 |
additional span handling |
To Reproduce
- Install
azure-ai-projects==2.0.1 with azure-core-tracing-opentelemetry
- Enable OpenTelemetry tracing with a
TracerProvider
- Trigger a scenario where
azure-core-tracing-opentelemetry returns a NonRecordingSpan (e.g., tracing disabled, invalid span context, or no active trace)
- Make any LLM call through the Responses API
- Observe crash:
AttributeError: 'NonRecordingSpan' object has no attribute 'attributes'
Expected behavior
is_recording should be called as is_recording() so it returns False for non-recording spans, and the guard properly skips instrumentation. No crash should occur.
Additional context
The fix is a one-character change at each site — add () to call the method:
- if not span or not span.span_instance.is_recording:
+ if not span or not span.span_instance.is_recording():
We are currently working around this with a monkey-patch on _append_to_message_attribute:
from azure.ai.projects.telemetry import _responses_instrumentor as mod
impl_cls = mod._ResponsesInstrumentorPreview
original = impl_cls._append_to_message_attribute
def _safe_append(self, span, attribute_name, new_messages):
span_inst = getattr(span, "span_instance", None)
if span_inst is None or not hasattr(span_inst, "attributes"):
return
return original(self, span, attribute_name, new_messages)
impl_cls._append_to_message_attribute = _safe_append
The is_recording property vs method inconsistency is a known footgun in the OpenTelemetry Python SDK — Span.is_recording() is a method, but it reads like a property so it's easy to forget the parens.
PR with the fix incoming.
Describe the bug
_ResponsesInstrumentorPreviewinazure.ai.projects.telemetry._responses_instrumentorchecksspan.span_instance.is_recordingwithout parentheses at 15 call sites. Sinceis_recordingis a method on OpenTelemetry spans (not a property), the expression evaluates to a bound method object which is always truthy — the guard never fires.When the underlying span is a
NonRecordingSpan(returned byazure-core-tracing-opentelemetrywhen tracing is disabled or the span context is invalid), downstream code accesses.attributeswhich only exists on SDKReadableSpan, crashing every LLM call with:The bug is present at these 15 sites in
_responses_instrumentor.py:_set_span_attribute_safeTo Reproduce
azure-ai-projects==2.0.1withazure-core-tracing-opentelemetryTracerProviderazure-core-tracing-opentelemetryreturns aNonRecordingSpan(e.g., tracing disabled, invalid span context, or no active trace)AttributeError: 'NonRecordingSpan' object has no attribute 'attributes'Expected behavior
is_recordingshould be called asis_recording()so it returnsFalsefor non-recording spans, and the guard properly skips instrumentation. No crash should occur.Additional context
The fix is a one-character change at each site — add
()to call the method:We are currently working around this with a monkey-patch on
_append_to_message_attribute:The
is_recordingproperty vs method inconsistency is a known footgun in the OpenTelemetry Python SDK —Span.is_recording()is a method, but it reads like a property so it's easy to forget the parens.PR with the fix incoming.