Repository navigation
Mutable span and transaction options can affect already-created tracing objects #6416
Description
Summary
SpanOptions and TransactionOptions are mutable configuration objects, but the SDK retains the caller-owned instances after creating a span or transaction. Consequently, modifying or reusing an options instance can change the behavior of an already-running span or transaction.
The SDK also mutates caller-provided SpanOptions in the timestamp-bearing startChild overloads. This can leak a timestamp into later calls that reuse the same options object.
This was surfaced while removing Instrumenter in #6252, where ActivityLifecycleIntegration reuses one SpanOptions instance for app-start, TTID, and TTFD spans.
Reproduction
Caller-visible mutation and stale timestamps
SpanOptions options = new SpanOptions();
parent.startChild("first", "description", explicitTimestamp, Instrumenter.SENTRY, options);
// The SDK has modified caller-owned state.
assert options.getStartTimestamp() == explicitTimestamp;
// This may unexpectedly reuse explicitTimestamp instead of starting now.
parent.startChild("second", "description", options);Existing span behavior can change
SpanOptions options = new SpanOptions();
ISpan span = parent.startChild("operation", "description", options);
// Span retains the same options instance and reads trimming flags when finishing.
options.setTrimEnd(true);
span.finish();The same applies to TransactionOptions: SentryTracer retains the supplied instance and continues reading values such as idleTimeout, deadlineTimeout, waitForChildren, and transactionFinishedCallback throughout the transaction lifetime.
Expected behavior
Options should be treated as point-in-time creation configuration:
- The SDK should not mutate caller-provided options.
- Changes to an options object after
startChildorstartTransactionshould not affect the created span or transaction. - Reusing an options instance should not leak state between spans or transactions.
- Native and OpenTelemetry implementations should have consistent behavior.
Actual behavior
Spanretains its suppliedSpanOptionsand reads some fields later.SentryTracerretains its suppliedTransactionOptionsand reads some fields later.- Timestamp-bearing
startChildimplementations mutate the suppliedSpanOptions. - Reusing or concurrently sharing an options instance can produce stale or inconsistent behavior.
Relevant locations
sentry/src/main/java/io/sentry/Span.javasentry/src/main/java/io/sentry/SentryTracer.javasentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OtelSpanWrapper.javasentry-android-core/src/main/java/io/sentry/android/core/ActivityLifecycleIntegration.java
Acceptance criteria
- Span and transaction creation snapshot the relevant options rather than retaining caller-owned mutable state.
- No
startChildimplementation modifies a suppliedSpanOptionsinstance. - Tests cover options reuse, mutation after creation, and native/OpenTelemetry consistency.
Activity
linear-code commentedon Oct 29, 2026
JAVA-820
fix(opentelemetry): Align null timestamp handling