1+ import type { TracerProvider } from '@opentelemetry/api' ;
12import { context , propagation , trace } from '@opentelemetry/api' ;
23import type { SpanProcessor } from '@opentelemetry/sdk-trace-base' ;
34import { BasicTracerProvider } from '@opentelemetry/sdk-trace-base' ;
@@ -26,6 +27,41 @@ import { getOpenTelemetryInstrumentationToPreload } from '../integrations/tracin
2627// About 277h - this must fit into new Array(len)!
2728const MAX_MAX_SPAN_WAIT_DURATION = 1_000_000 ;
2829
30+ // The global registry of @opentelemetry /api 1.x, shared across all copies of the package
31+ const OTEL_API_GLOBAL_KEY = Symbol . for ( 'opentelemetry.js.api.1' ) ;
32+
33+ /**
34+ * Registers the given tracer provider as the global tracer provider, recreating the OpenTelemetry
35+ * API registry when it pre-exists with a different `@opentelemetry/api` version.
36+ *
37+ * Some host runtimes (e.g. Neon Functions) pre-create the registry with their own API version.
38+ * `registerGlobal` requires an exact version match, so every registration through the SDK's copy
39+ * of `@opentelemetry/api` is rejected and tracing is silently disabled
40+ * (https://github.com/getsentry/sentry-javascript/issues/22338). This case is identified by a
41+ * failed registration with an empty `trace` slot: `registerGlobal` checks the slot before the
42+ * version, so an empty slot means the version gate rejected us and recreating the registry
43+ * clobbers no other tracer provider. If the slot is occupied (another provider registered first,
44+ * e.g. a second `Sentry.init()` call), the registry is left untouched and registration fails.
45+ */
46+ function registerGlobalTracerProvider ( provider : TracerProvider ) : boolean {
47+ if ( trace . setGlobalTracerProvider ( provider ) ) {
48+ return true ;
49+ }
50+
51+ const otelGlobal = globalThis as unknown as Record < symbol , { trace ?: unknown } | undefined > ;
52+ const registry = otelGlobal [ OTEL_API_GLOBAL_KEY ] ;
53+ if ( registry && ! registry . trace ) {
54+ DEBUG_BUILD &&
55+ coreDebug . warn (
56+ 'Replaced a pre-existing OpenTelemetry API registry that was created by a different @opentelemetry/api version and would have blocked tracing. If you want to manage OpenTelemetry yourself, set `skipOpenTelemetrySetup: true` in `Sentry.init()`.' ,
57+ ) ;
58+ otelGlobal [ OTEL_API_GLOBAL_KEY ] = undefined ;
59+ return trace . setGlobalTracerProvider ( provider ) ;
60+ }
61+
62+ return false ;
63+ }
64+
2965interface AdditionalOpenTelemetryOptions {
3066 /** Additional SpanProcessor instances that should be used. */
3167 spanProcessors ?: SpanProcessor [ ] ;
@@ -118,7 +154,7 @@ export function setupOtel(
118154 } ) ;
119155
120156 // Register as globals
121- trace . setGlobalTracerProvider ( provider ) ;
157+ registerGlobalTracerProvider ( provider ) ;
122158 propagation . setGlobalPropagator ( new SentryPropagator ( ) ) ;
123159
124160 const ctxManager = new SentryContextManager ( ) ;
@@ -132,7 +168,7 @@ function setupSentryTracerProvider(
132168) : [ SentryTracerProvider | undefined , AsyncLocalStorageLookup | undefined ] {
133169 const provider = new SentryTracerProvider ( { resource : getSentryResource ( 'node' ) } ) ;
134170
135- if ( ! trace . setGlobalTracerProvider ( provider ) ) {
171+ if ( ! registerGlobalTracerProvider ( provider ) ) {
136172 DEBUG_BUILD &&
137173 coreDebug . warn (
138174 'Could not register SentryTracerProvider because another OpenTelemetry tracer provider is already registered.' ,
0 commit comments