@@ -71,18 +71,14 @@ export function startSpan<T>(options: StartSpanOptions, callback: (span: Span) =
7171
7272 const missingRequiredParent = options . onlyIfParent && ! parentSpan ;
7373 const activeSpan = missingRequiredParent
74- ? new SentryNonRecordingSpan ( { traceId : scope . getPropagationContext ( ) . traceId } )
74+ ? startMissingRequiredParentSpan ( scope , client )
7575 : createChildOrRootSpan ( {
7676 parentSpan,
7777 spanArguments,
7878 forceTransaction,
7979 scope,
8080 } ) ;
8181
82- if ( missingRequiredParent ) {
83- client ?. recordDroppedEvent ( 'no_parent_span' , 'span' ) ;
84- }
85-
8682 // Ignored root spans still need to be set on scope so that `getActiveSpan()` returns them
8783 // and descendants are also non-recording. Ignored child spans don't need this because
8884 // the parent span is already on scope.
@@ -138,18 +134,14 @@ export function startSpanManual<T>(options: StartSpanOptions, callback: (span: S
138134
139135 const missingRequiredParent = options . onlyIfParent && ! parentSpan ;
140136 const activeSpan = missingRequiredParent
141- ? new SentryNonRecordingSpan ( { traceId : scope . getPropagationContext ( ) . traceId } )
137+ ? startMissingRequiredParentSpan ( scope , getClient ( ) )
142138 : createChildOrRootSpan ( {
143139 parentSpan,
144140 spanArguments,
145141 forceTransaction,
146142 scope,
147143 } ) ;
148144
149- if ( missingRequiredParent ) {
150- getClient ( ) ?. recordDroppedEvent ( 'no_parent_span' , 'span' ) ;
151- }
152-
153145 // We don't set ignored child spans onto the scope because there likely is an active,
154146 // unignored span on the scope already.
155147 if ( ! _isIgnoredSpan ( activeSpan ) || ! parentSpan ) {
@@ -208,8 +200,7 @@ export function startInactiveSpan(options: StartSpanOptions): Span {
208200 const missingRequiredParent = options . onlyIfParent && ! parentSpan ;
209201
210202 if ( missingRequiredParent ) {
211- client ?. recordDroppedEvent ( 'no_parent_span' , 'span' ) ;
212- return new SentryNonRecordingSpan ( { traceId : scope . getPropagationContext ( ) . traceId } ) ;
203+ return startMissingRequiredParentSpan ( scope , client ) ;
213204 }
214205
215206 return createChildOrRootSpan ( {
@@ -332,6 +323,19 @@ export function startNewTrace<T>(callback: () => T): T {
332323 } ) ;
333324}
334325
326+ /**
327+ * The placeholder returned from `startSpan*` when `onlyIfParent` is set but there is no parent span.
328+ * It carries the current trace id and captured scopes so the trace data it propagates (and any nested
329+ * span that resolves it as its root via `getRootSpan`) reads its DSC from the scope, preserving a
330+ * continued trace's DSC instead of fabricating a fresh client one. Also records the dropped-span outcome.
331+ */
332+ function startMissingRequiredParentSpan ( scope : Scope , client : Client | undefined ) : SentryNonRecordingSpan {
333+ client ?. recordDroppedEvent ( 'no_parent_span' , 'span' ) ;
334+ const span = new SentryNonRecordingSpan ( { traceId : scope . getPropagationContext ( ) . traceId } ) ;
335+ setCapturedScopesOnSpan ( span , scope , getIsolationScope ( ) ) ;
336+ return span ;
337+ }
338+
335339function createChildOrRootSpan ( {
336340 parentSpan,
337341 spanArguments,
0 commit comments