Describe your environment
opentelemetry-sdk 1.44.0, CPython 3.11. Also present at main (opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py, Span.set_status).
What happened?
set_status guards two cases when it is handed a Status instance: it returns early if the span is already OK, and it returns early if the incoming status is UNSET. Nothing guards a second ERROR overwriting a first one, so self._status = status runs unconditionally and a bare Status(StatusCode.ERROR) with no description silently replaces Status(StatusCode.ERROR, "..."). The status code is unchanged, so nothing looks wrong; only the description disappears.
This is reachable without anyone doing something odd. A library sets a specific error on a span, and later generic error handling, exception bookkeeping or an instrumentation helper sets a plain ERROR on the same span. The specific message is the one that had diagnostic value, and it is the one that is lost.
Steps to reproduce
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.trace import Status, StatusCode
span = TracerProvider().get_tracer(__name__).start_span("demo")
span.set_status(Status(StatusCode.ERROR, "connection refused to db-1"))
span.set_status(Status(StatusCode.ERROR))
print(span.status.description)
What did you expect to see?
connection refused to db-1.
What did you see instead?
None.
Suggested fix
In the isinstance(status, Status) branch, decline the assignment when the incoming status carries no description and the existing status has the same code and does have one. That keeps the first, more specific description without changing the status code, and it leaves every other transition alone, including a genuine re-description of an existing error.
It is a small change and I have it written, so a PR will follow this issue rather than wait on agreement. If maintainers would rather the precedence sat somewhere else, or would rather it not change at all, say so there and I will rework or close it.
Additional context
This surfaced from open-telemetry/opentelemetry-python-contrib#4769, where I first tried to handle it in the shared HTTP semconv helper by reading span.status before setting it. @herin049 pointed out, correctly, that the trace API does not expose a readable status on Span at all, only the SDK's ReadableSpan does, so that fix depended on an implementation detail and put precedence logic in two places that could disagree. Raising it here instead, where set_status already owns precedence.
Describe your environment
opentelemetry-sdk1.44.0, CPython 3.11. Also present atmain(opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py,Span.set_status).What happened?
set_statusguards two cases when it is handed aStatusinstance: it returns early if the span is alreadyOK, and it returns early if the incoming status isUNSET. Nothing guards a secondERRORoverwriting a first one, soself._status = statusruns unconditionally and a bareStatus(StatusCode.ERROR)with no description silently replacesStatus(StatusCode.ERROR, "..."). The status code is unchanged, so nothing looks wrong; only the description disappears.This is reachable without anyone doing something odd. A library sets a specific error on a span, and later generic error handling, exception bookkeeping or an instrumentation helper sets a plain
ERRORon the same span. The specific message is the one that had diagnostic value, and it is the one that is lost.Steps to reproduce
What did you expect to see?
connection refused to db-1.What did you see instead?
None.Suggested fix
In the
isinstance(status, Status)branch, decline the assignment when the incoming status carries no description and the existing status has the same code and does have one. That keeps the first, more specific description without changing the status code, and it leaves every other transition alone, including a genuine re-description of an existing error.It is a small change and I have it written, so a PR will follow this issue rather than wait on agreement. If maintainers would rather the precedence sat somewhere else, or would rather it not change at all, say so there and I will rework or close it.
Additional context
This surfaced from open-telemetry/opentelemetry-python-contrib#4769, where I first tried to handle it in the shared HTTP semconv helper by reading
span.statusbefore setting it. @herin049 pointed out, correctly, that the trace API does not expose a readable status onSpanat all, only the SDK'sReadableSpandoes, so that fix depended on an implementation detail and put precedence logic in two places that could disagree. Raising it here instead, whereset_statusalready owns precedence.