Skip to content

[SDK] Remove noexcept from the SDK Span constructor - #4700

Open
akshat-lakhera wants to merge 1 commit into
open-telemetry:mainfrom
akshat-lakhera:fix/4685-span-noexcept
Open

akshat-lakhera wants to merge 1 commit into
open-telemetry:mainfrom
akshat-lakhera:fix/4685-span-noexcept

Conversation

@akshat-lakhera

Copy link
Copy Markdown

Fixes #4685

Changes

The SDK Span constructor was marked noexcept but allocates (recordable, name, attributes, links), so a bad_alloc would call std::terminate and take down the host application. Tracer::StartSpan already catches exceptions and returns a no-op span, so this removes noexcept from the constructor and lets the exception reach that handler. Builds without exceptions are unchanged.

No unit test added: every callback the constructor invokes is itself noexcept, so a bad_alloc cannot be injected without terminating.

For significant contributions please make sure you have completed the following items:

  • CHANGELOG.md updated for non-trivial changes
  • Unit tests have been added
  • Changes in public API reviewed

@akshat-lakhera
akshat-lakhera requested a review from a team as a code owner October 13, 2026 06:49
@codecov

codecov Bot commented Oct 13, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 54.76190% with 19 lines in your changes missing coverage. Please review.
✅ Project coverage is 86.77%. Comparing base (935b878) to head (536f475).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
exporters/ostream/src/span_exporter.cc 25.00% 3 Missing ⚠️
exporters/otlp/src/otlp_file_exporter.cc 25.00% 3 Missing ⚠️
exporters/otlp/src/otlp_grpc_exporter.cc 50.00% 3 Missing ⚠️
exporters/otlp/src/otlp_http_exporter.cc 50.00% 3 Missing ⚠️
exporters/zipkin/src/zipkin_exporter.cc 25.00% 3 Missing ⚠️
...lemetry/exporters/memory/in_memory_span_exporter.h 33.34% 2 Missing ⚠️
sdk/src/trace/multi_recordable.cc 33.34% 2 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #4700      +/-   ##
==========================================
- Coverage   86.81%   86.77%   -0.04%     
==========================================
  Files         525      525              
  Lines       20736    20759      +23     
==========================================
+ Hits        18000    18011      +11     
- Misses       2736     2748      +12     
Files with missing lines Coverage Δ
...ude/opentelemetry/sdk/trace/multi_span_processor.h 100.00% <100.00%> (ø)
...lemetry/exporters/memory/in_memory_span_exporter.h 81.82% <33.34%> (-8.18%) ⬇️
sdk/src/trace/multi_recordable.cc 97.27% <33.34%> (+1.49%) ⬆️
exporters/ostream/src/span_exporter.cc 79.63% <25.00%> (-2.27%) ⬇️
exporters/otlp/src/otlp_file_exporter.cc 87.81% <25.00%> (-6.93%) ⬇️
exporters/otlp/src/otlp_grpc_exporter.cc 76.15% <50.00%> (-2.15%) ⬇️
exporters/otlp/src/otlp_http_exporter.cc 97.91% <50.00%> (-1.24%) ⬇️
exporters/zipkin/src/zipkin_exporter.cc 85.08% <25.00%> (-3.98%) ⬇️

... and 2 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@lalitb

lalitb commented Oct 14, 2026

Copy link
Copy Markdown
Member

@akshat-lakhera - Thanks for working on this. MakeRecordable() is still noexcept, so an allocation failure there terminates before it can reach StartSpan’s catch. Removing noexcept from the Span constructor doesn’t change that path.

Could we handle the failure inside MakeRecordable() and add a regression test? The Span constructor already handles a null recordable.

@linux-foundation-easycla

linux-foundation-easycla Bot commented Oct 14, 2026 •

Copy link
Copy Markdown

CLA Signed
The committers listed above are authorized under a signed CLA.

@akshat-lakhera

Copy link
Copy Markdown
Author

@lalitb Updated in f167d33. The Span constructor keeps noexcept now, and the allocation failure is handled inside MakeRecordable() instead: MultiSpanProcessor and the span exporters (ostream, in-memory, OTLP gRPC/HTTP/file, Zipkin) catch it and return nullptr, which the Span constructor already treats as a non-recording span. MultiSpanProcessor also skips a child that returns null, since MultiRecordable forwards every setter to its children and would dereference it.

Added MultiSpanProcessorTest.MakeRecordableSkipsChildWithoutRecordable as a regression test. It segfaults on the old code and passes now.

@lalitb

lalitb commented Oct 14, 2026

Copy link
Copy Markdown
Member

Looks good overall. One small suggestion: could we add a test that forces the span allocation to fail and verifies that extraction preserves the original context?

Since noexcept handling is being discussed in #3013, @marcalff, it would be helpful to get your view on the new allocation failure fallback here.

@akshat-lakhera

Copy link
Copy Markdown
Author

@lalitb added Tracer.FailedSpanAllocationPreservesContextOnExtract in tracer_test.cc. It makes the MultiRecordable allocation throw bad_alloc once through a replacement operator new (only when exceptions are enabled), then checks that StartSpan returns a non-recording span that still has the parent trace id, a new span id and the sampled flag. After injecting it with HttpTraceContext and extracting it again, the trace id, span id and flags are unchanged, and the next span on the tracer records normally. Without the MakeRecordable change the test terminates on the bad_alloc. Also rebased onto main; the only conflict was CHANGELOG.md.

Signed-off-by: Akshat Lakhera <akshatlnct2024@gmail.com>

This branch has not been deployed

No deployments
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.

Span construction might result in a terminate

2 participants