open-telemetry / open-telemetry/opentelemetry-python

Self-referential attribute value crashes the instrumented application with RecursionError

Open
#5,564 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug
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

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.