You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Self-referential attribute value crashes the instrumented application with RecursionError #5564
_clean_attribute_value walks nested Sequence and Mapping attribute values recursively with no cycle detection and no depth bound. A self-referential list or dict recurses until the interpreter stack is exhausted, and the resulting RecursionError propagates out of the public telemetry API into the calling application.
This became reachable when AnyValue was widened to accept arbitrarily nested values. Previously such an object would simply have been stringified.
Steps to Reproduce
fromopentelemetry.sdk.traceimportTracerProvidercyclic= [1, 2]
cyclic.append(cyclic) # a list that contains itselftracer=TracerProvider().get_tracer(__name__)
withtracer.start_as_current_span("span") asspan:
span.set_attribute("k", cyclic)
Expected Result
The unusable value is dropped and recorded as None with a warning, the surrounding attributes are preserved, and no exception reaches the caller.
Actual Result
RecursionError: maximum recursion depth exceeded
# every public entry point is affected
span.set_attribute('k', cyclic_list) RecursionError ESCAPES into caller
span.set_attribute('k', cyclic_dict) RecursionError ESCAPES into caller
span.set_attributes({'k': cyclic_list}) RecursionError ESCAPES into caller
span.add_event('e', {'k': cyclic_list}) RecursionError ESCAPES into caller
Resource.create({'k': cyclic_list}) RecursionError ESCAPES into caller
counter.add(1, {'k': cyclic_list}) RecursionError ESCAPES into caller
logger.emit(attributes={'k': cyclic_list}) RecursionError ESCAPES into caller
# a deeply nested but acyclic value hits the same wall
span.set_attribute('k', 2000-deep nested list) RecursionError ESCAPES into caller
Additional context
An observability library can take down the process it is observing. The value does not have to be deliberately cyclic: any object graph with a back-reference - a parent pointer, a memoisation cache, a linked structure - will do, and AnyValue now invites callers to pass exactly those. Because the exception surfaces at the instrumentation call site rather than in an exporter thread, it is not caught by the SDK's usual broad exception handling.
Would you like to implement a fix?
Yes
Tip
React with 👍 to help prioritize this issue. Please use comments to provide useful context, avoiding +1 or me too, to help us triage it. Learn more here.
Activity