diff --git a/packages/opentelemetry/src/sampler.ts b/packages/opentelemetry/src/sampler.ts index b5cfc2ee5514..316c7e22bbc3 100644 --- a/packages/opentelemetry/src/sampler.ts +++ b/packages/opentelemetry/src/sampler.ts @@ -48,7 +48,7 @@ export class SentrySampler implements Sampler { ): SamplingResult { const options = this._client.getOptions(); - const parentSpan = trace.getSpan(context); + const parentSpan = getValidSpan(context); const parentContext = parentSpan?.spanContext(); if (!hasTracingEnabled(options)) { @@ -210,3 +210,12 @@ function getBaseTraceState(context: Context, spanAttributes: SpanAttributes): Tr return traceState; } + +/** + * If the active span is invalid, we want to ignore it as parent. + * This aligns with how otel tracers and default samplers handle these cases. + */ +function getValidSpan(context: Context): Span | undefined { + const span = trace.getSpan(context); + return span && isSpanContextValid(span.spanContext()) ? span : undefined; +} diff --git a/packages/opentelemetry/test/trace.test.ts b/packages/opentelemetry/test/trace.test.ts index 0a709c9fb5da..dbdc588bd328 100644 --- a/packages/opentelemetry/test/trace.test.ts +++ b/packages/opentelemetry/test/trace.test.ts @@ -1450,6 +1450,27 @@ describe('trace (sampling)', () => { }, }); }); + + it('ignores parent span context if it is invalid', () => { + mockSdkInit({ tracesSampleRate: 1 }); + const traceId = 'd4cda95b652f4a1592b449d5929fda1b'; + + const spanContext = { + traceId, + spanId: 'INVALID', + traceFlags: TraceFlags.SAMPLED, + }; + + context.with(trace.setSpanContext(ROOT_CONTEXT, spanContext), () => { + startSpan({ name: 'outer' }, span => { + expect(span.isRecording()).toBe(true); + expect(span.spanContext().spanId).not.toBe('INVALID'); + expect(span.spanContext().spanId).toMatch(/[a-f0-9]{16}/); + expect(span.spanContext().traceId).not.toBe(traceId); + expect(span.spanContext().traceId).toMatch(/[a-f0-9]{32}/); + }); + }); + }); }); describe('HTTP methods (sampling)', () => {