Describe your environment
Labels: bug, api, sdk, crash
Affected packages: opentelemetry-api, opentelemetry-sdk
Found on: main @ 0a5d76b6
Environment: CPython 3.12
What happened?
_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
from opentelemetry.sdk.trace import TracerProvider
cyclic = [1, 2]
cyclic.append(cyclic) # a list that contains itself
tracer = TracerProvider().get_tracer(__name__)
with tracer.start_as_current_span("span") as span:
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.
Describe your environment
Labels: bug, api, sdk, crash
Affected packages:
opentelemetry-api,opentelemetry-sdkFound on:
main@0a5d76b6Environment: CPython 3.12
What happened?
_clean_attribute_valuewalks nestedSequenceandMappingattribute 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 resultingRecursionErrorpropagates out of the public telemetry API into the calling application.This became reachable when
AnyValuewas widened to accept arbitrarily nested values. Previously such an object would simply have been stringified.Steps to Reproduce
Expected Result
The unusable value is dropped and recorded as
Nonewith a warning, the surrounding attributes are preserved, and no exception reaches the caller.Actual Result
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
AnyValuenow 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
+1orme too, to help us triage it. Learn more here.