Skip to content

[azure-ai-projects] ResponsesInstrumentor crashes on NonRecordingSpan — is_recording not called as method #46544

@GitAashishG

Description

@GitAashishG
  • 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

  1. Install azure-ai-projects==2.0.1 with azure-core-tracing-opentelemetry
  2. Enable OpenTelemetry tracing with a TracerProvider
  3. Trigger a scenario where azure-core-tracing-opentelemetry returns a NonRecordingSpan (e.g., tracing disabled, invalid span context, or no active trace)
  4. Make any LLM call through the Responses API
  5. 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.

Metadata

Metadata

Assignees

Labels

AI Projectscustomer-reportedIssues that are reported by GitHub users external to the Azure organization.needs-team-attentionWorkflow: This issue needs attention from Azure service team or SDK teamquestionThe issue doesn't require a change to the product in order to be resolved. Most issues start as that

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions