open-telemetry / open-telemetry/opentelemetry-python
Self-referential attribute value crashes the instrumented application with RecursionError
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 2.6k
- Forks
- 1k
- Avg merge
- 4d 15h
- Merged PRs (30d)
- 19
Description
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.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by locating _clean_attribute_value in the opentelemetry-api and opentelemetry-sdk packages, then trace its use from span attributes, events, resources, metrics, and logs. Reproduce the cyclic and deeply nested examples first; done means invalid values are recorded as None with a warning, surrounding attributes remain usable, and no RecursionError reaches callers.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- observability
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 74/100