Repository navigation
Span streaming drops continued traces without baggage: empty frozen DSC leads to missing_dsc (breaks Uptime check traces) #25243
Description
Is there an existing issue for this?
- I have checked for existing issues https://github.com/getsentry/sentry-javascript/issues
- I have reviewed the documentation https://docs.sentry.io/
- I am using the latest SDK release https://github.com/getsentry/sentry-javascript/releases
How do you use Sentry?
Sentry Saas (sentry.io)
Which SDK are you using?
@sentry/node
SDK Version
11.6.0
Framework Version
Node.js 24.21.0 (first seen in production with @sentry/nestjs 11.0.0 on Fastify and @sentry/react-router 11.0.0)
Link to Sentry event
No response
Reproduction Example/SDK Setup
import http from 'node:http';
import * as Sentry from '@sentry/node';
Sentry.init({
dsn: 'https://public@o123.ingest.de.sentry.io/1',
tracesSampleRate: 1,
transport: (options) => Sentry.createTransport(options, () => Promise.resolve({ statusCode: 200 })),
});
Sentry.getClient().on('beforeEnvelope', ([header, items]) => {
if (items.some(([item]) => item.type === 'span')) console.log(header.trace ? 'trace header present' : 'NO trace header');
});
const server = http.createServer((req, res) => res.end('ok'));
server.listen(0, async () => {
await fetch(`http://127.0.0.1:${server.address().port}/`, {
headers: { 'sentry-trace': `${'a'.repeat(32)}-${'b'.repeat(16)}-1` },
});
await Sentry.flush(2000);
server.close();
});Steps to Reproduce
- Run the script above with
@sentry/node11.6.0 (defaulttraceLifecycle: 'stream'). - It sends one request to its own server with a sampled
sentry-traceheader and nobaggageheader. - Look at the logged envelope headers: the envelope with the incoming request's spans has no
traceheader. (The second envelope belongs to the outgoingfetch, which starts its own trace.) - Add
strictTraceContinuation: truetoSentry.initand run it again: now that envelope has atraceheader.
Expected Result
Spans of a request that continues a trace from a sentry-trace header without baggage reach Sentry, as the equivalent transactions did with SDK 10. For example the SDK could fill the DSC from the client (trace_id, public_key) in stream mode, Relay could accept such span envelopes, or the uptime checker could send baggage.
Actual Result
The span envelope of the incoming request goes out without a trace header, and Relay drops it. The org stats show those spans with outcome invalid, reason missing_dsc.
In production this hits Sentry Uptime monitoring: an uptime check on a readiness endpoint (trace sampling on) reaches two Node services every five minutes and gets a 200, but since the upgrade every span of those requests is dropped as missing_dsc (12 spans per check in one project, 14 in the other). A request to the same endpoint without trace headers is traced and accepted end to end. The check's requests apparently carry sentry-trace without Sentry baggage.
Additional Context
Where it comes from, in @sentry/core 11.6.0:
propagationContextFromHeaders(build/esm/utils/tracing.js) returnsdsc: dynamicSamplingContext || {}when the traceparent has a trace id but no DSC came with it ("we are not head of trace and we must freeze it").createStreamedSpanEnvelope(build/esm/tracing/spans/envelope.js) adds thetraceenvelope header only whendscHasRequiredProps(dsc)holds, which needstrace_idandpublic_key.
Workaround: strictTraceContinuation: true. The spans arrive again, but the services no longer continue the uptime check's trace, so the check and its downstream spans end up in separate traces.
Priority
No response
Metadata
Metadata
Assignees
Projects
- StatusShow more project fieldsNo status
Activity
linear-code commentedon Oct 9, 2026
JS-3905
Lms24 commentedon Oct 12, 2026
Hey @reichhartd thanks for reporting this! It appears this is a bug in our ingestion service Relay, rather than v11 of the SDK. In fact, v11 "accidentally" fixed a bug we had in v10 that caused a
traceheader to be sent along, even if an incoming request has nobaggageheader. As per Sentry's DSC spec, not sending atraceenvelope header in this specific case is expected behaviour (i.e. we "freeze" an empty DSC onto the service span we continue the trace from.I will discuss this with the Ingest team to see if we need to make adjustments in Relay, or if we need to fix this SDK-side.
Relevant Relay PR: getsentry/relay#5276
Having
strictTracePropagationturned on, means the SDK decides against continuing an incoming trace, since there is nobaggageheader which carries theorgIdto compare against the local org id. In this case, the DSC is computed from the local SDK and thus it is "valid" again.My question is: Do you know why your service is receiving requests with a
sentry-tracebut not abaggageheader? The case we foresaw this happening in is when old SDKs (by now really old SDKs) that didn't implement ourbaggagepropagation mechanism would send. Curious if this is the case for you or if there's another reason.