open-telemetry / open-telemetry/opentelemetry-python

Span is already immutable during OnEnding span processor callbacks, violating the spec

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

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:

https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/sdk.md#onending

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

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 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.