Repository navigation
Memory leak: frozen Nest Create Nest App span retains unbounded _sentryChildSpans (SDK ≥10.64; mitigated by openTelemetryBasicTracerProvider: true, which #22557 removes) #22860
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?
Self-hosted/on-premise
Which SDK are you using?
@sentry/nestjs
SDK Version
10.66.0
Framework Version
NestJS 11
Link to Sentry event
No response
Reproduction Example/SDK Setup
SDK ≥10.64
Sentry.init({ debug: false, dsn: config.SENTRY_DSN, enabled: isPreproductionOrProduction(), enableMetrics: false, tracesSampleRate: 0, environment: config.NODE_ENV, release: version, })
Steps to Reproduce
- Nest app with
@sentry/nestjs(SDK ≥10.64) +SentryModule.forRoot(),Sentry.init({ tracesSampleRate: 0 }) - Run under sustained HTTP / messaging load for ≥1h
- Force GC and compare
old_space/ take heap snapshots
Expected Result
- An ended / frozen span should not keep accumulating children, or
_sentryChildSpansshould be cleared / bounded when a span ends, or- Bootstrap /
app_creationspans should not remain active parents after Nest boot completes
Actual Result
Ended Create Nest App span retains an unbounded _sentryChildSpans set for process lifetime → continuous Scope / SentryNonRecordingSpan growth even with tracesSampleRate: 0.
Additional Context
After upgrading from @sentry/nestjs@10.62.0, a long-lived Nest service’s JS heap grew ~80 MB/h under production traffic and never reclaimed on forced GC.
Heap snapshots ~ 70 minutes apart showed almost 1:1 growth of Scope and SentryNonRecordingSpan (~ +176k each). Retention was dominated by a single root span:
- name:
Create Nest App - op / attrs:
app_creation.nestjs,sentry.origin=auto.http.otel.nestjs,nestjs.module=AppModule _sentryChildSpans: ~154k direct children (of ~233k total child links in the heap)- Span also carried
sentry.tracerProviderSpan(marker for spans from Sentry’s minimal tracer provider)
Important: this root span was already ended / frozen (_frozen: true, _status set). It still kept receiving children via addChildSpanToSpan into a strong Set (_sentryChildSpans), so memory grew for the lifetime of the process.
Observed constructor deltas (shallow, ~70 min):
| Constructor | Δ count | Δ shallow |
|---|---|---|
Object |
+1.09M | +55 MB |
Scope |
+176k | +31 MB |
Array |
+709k | +22 MB |
SentryNonRecordingSpan |
+176k | +19 MB |
WeakRef |
+528k | +16 MB |
Dominant retainer shape:
Scope ←_sentryScope— SentryNonRecordingSpan ←(Set)— _sentryChildSpans ← SentrySpan("Create Nest App")
Same growth pattern appeared on a second service after the same SDK bump, at a lower rate matching lower traffic — points away from app-specific code.
Suspected regression
10.64.0 introduced default use of Sentry’s minimal SentryTracerProvider instead of the full OTel BasicTracerProvider:
- https://github.com/getsentry/sentry-javascript/blob/HEAD/CHANGELOG.md (10.64.0)
- PRs: feat(opentelemetry): Add SentryTracerProvider #21666, feat(node): Wire up SentryTracerProvider #21680, feat(core): Seal SentryTracerProvider spans against mutation after they end #21842
The leaking root is created by Nest instrumentation (getAppCreationSpanOptions / orchestrion NESTJS_APP_CREATION → startInactiveSpan). Children are attached via addChildSpanToSpan in @sentry/core with no guard for an already-ended parent and no upper bound on _sentryChildSpans.
Related prior discussion (different trigger, same “never-pruned root span” failure mode): #13412
Upstream WeakRef work (#21242) is present in 10.66 but does not stop strong retention through _sentryChildSpans.
Workaround (confirmed on our prod)
Sentry.init({
// ...
openTelemetryBasicTracerProvider: true,
})After deploying this, container memory on the affected Nest service returned to a flat profile.
The httpIntegration({ ignoreOutgoingRequests: () => true }) workaround from #13412 does not apply: the accumulator is the Nest app_creation span, not an outgoing HTTP/WebSocket span.
Note on the workaround's lifetime
openTelemetryBasicTracerProvider: true is currently our only mitigation. #22557 (merged to develop, not yet in a published release as of 10.69.0) removes this option along with SentrySpanProcessor / SentrySampler.
Once that lands, TypeScript will reject the option as unknown. Deleting it to silence the error silently reintroduces the leak, because initOtel no longer has an opt-out and the minimal SentryTracerProvider becomes the only path.
If the accumulation is inherent to SentryTracerProvider, removing the opt-out leaves affected Nest services with no mitigation short of disabling Sentry. Relevant to the direction in #22486.
Happy to provide redacted heap-snapshot excerpts / retainer dumps if needed.
Priority
React with 👍 to help prioritize this issue. Please use comments to provide useful context, avoiding +1 or me too, to help us triage it.
Metadata
Metadata
Assignees
Projects
- StatusShow more project fieldsNo status
Activity
linear-code commentedon Jul 30, 2026
JS-3235
andreiborza commentedon Jul 30, 2026
Hi @m3Lith, thanks for filing this.
The flag was removed on develop because it's our next major branch. This will not be released any time soon so no worries.
We'll look into this meanwhile.
fix(core): Bound child span tracking on long-lived spans (#22916)