open-telemetry / open-telemetry/opentelemetry-python
Span is already immutable during OnEnding span processor callbacks, violating the spec
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
OS: any
Python version: any
SDK version: 1.44.0 (present since the hook was added in #4775)
API version: 1.44.0
What happened?
The trace SDK spec says the span MUST still be mutable while OnEnding span processor callbacks run — the whole point of the hook is to allow last-moment modifications before export, with only the end timestamp already set:
In the Python SDK, Span.end() freezes the span before invoking _on_ending: it sets _end_time and marks the attributes immutable first, then calls the callbacks. All mutators (set_attribute, set_attributes, add_event, add_link, update_name, set_status) treat _end_time is not None as "span has ended" and silently no-op (they only log a warning), and is_recording() returns False. As a result, every mutation attempted from an _on_ending callback is dropped and never reaches the exported span, making the hook observe-only.
Steps to Reproduce
from opentelemetry.sdk.trace import TracerProvider, SpanProcessor
from opentelemetry.sdk.trace.export import SimpleSpanProcessor
from opentelemetry.sdk.trace.export.in_memory_span_exporter import InMemorySpanExporter
class MyProcessor(SpanProcessor):
def _on_ending(self, span):
span.set_attribute("on_ending.attr", "value")
exporter = InMemorySpanExporter()
provider = TracerProvider()
provider.add_span_processor(MyProcessor())
provider.add_span_processor(SimpleSpanProcessor(exporter))
provider.get_tracer(__name__).start_span("foo").end()
(span,) = exporter.get_finished_spans()
print(span.attributes) # {} — the attribute set in _on_ending is gone
Expected Result
The exported span contains on_ending.attr, and is_recording() returns True while the callback runs.
Actual Result
The mutation is silently dropped (Setting attribute on ended span. warning) and the exported span has no attributes.
Would you like to implement a fix?
Yes
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 at Span.end() and the _on_ending callback path in the Python SDK, then compare their behavior with the linked OpenTelemetry specification. Reproduce the issue using the provided MyProcessor example and verify that mutations are retained, is_recording() is true during the callback, and the exported span contains on_ending.attr.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- observability-sre
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 72/100