Skip to content

fix: drop None attributes from OTel span events - #190

Merged
lesnik512 merged 2 commits into
mainfrom
fix/otel-none-attributes
Oct 17, 2026
Merged

lesnik512 merged 2 commits into
mainfrom
fix/otel-none-attributes

Conversation

@lesnik512

Copy link
Copy Markdown
Member

_emit_event now leaves out None-valued attributes from the OTel span event. The log record still gets every attribute, None included.

Why

OpenTelemetry SDKs before 1.45 reject None attribute values. I reproduced it with an in-memory exporter, a recording span, and a sync Retry(max_attempts=2) that gives up on a raised ConnectError through MockTransport. On SDK 1.20.0, 1.38.0 and 1.44.0 every give-up logged

WARNING opentelemetry.attributes: Invalid type NoneType for attribute 'last_status' value. Expected one of ['bool', 'str', 'bytes', 'int', 'float'] or a sequence of those types

and the recorded retry.giving_up event had no last_status. SDK 1.45.1 accepts None and keeps it without a warning. Our floor is opentelemetry-api>=1.20, so most of the supported range is affected. With this change, every SDK version records the same event (without the None keys) and none of them log the warning. On 1.45+ that means last_status: None no longer shows up on the span event, so a missing key now means "no value".

Events that can carry None:

  • retry.giving_up (reason="max_attempts"): last_status when the last attempt raised instead of returning a response.
  • retry.budget_refused: last_status, same case.
  • circuit.rejected: retry_after when the circuit is half-open with a probe in flight.

bulkhead.rejected declares acquire_timeout: float | None, but it is only emitted when a bounded acquire times out, so it is never None in practice. The retry_after_exceeds_max_delay give-up always has a response.

The test follows the existing mock-span pattern in tests/test_observability.py, so no opentelemetry-sdk dev dependency was added.

Performance

_emit_event runs only when a resilience event fires, not on every request. The filter sits inside the OTel branch, so log-only installs don't pay for it. A dict comprehension over a six-key attribute dict costs about 0.6 to 0.7 us. Timing the whole _emit_event against a recording SDK span came out at 28 to 44 us per call before and after, and the run-to-run noise was bigger than the difference. On SDK 1.44 the None case got faster, from 42 to 54 us down to about 30 us, because the SDK warning is no longer logged.

Closes #171

OpenTelemetry SDKs before 1.45 reject None attribute values with a warning,
so a retry.giving_up after a raised NetworkError logged a warning and lost
last_status. The span event now leaves out None values; the log record
keeps them.

Closes #171
@lesnik512
lesnik512 merged commit 89553a6 into main Oct 17, 2026
13 checks passed
@lesnik512
lesnik512 deleted the fix/otel-none-attributes branch October 17, 2026 11:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

OTel span events may drop None-valued attributes with a warning

1 participant