Skip to content

fix(node): Register Vercel keep-alive listeners once - #25146

Merged
chargome merged 2 commits into
developfrom
fix/vercel-keepalive-repeated-init
Oct 8, 2026
Merged

chargome merged 2 commits into
developfrom
fix/vercel-keepalive-repeated-init

Conversation

@chargome

@chargome chargome commented Oct 7, 2026

Copy link
Copy Markdown
Member

On Vercel, each init() call registered another SIGTERM handler and another http.server.response.finish subscriber, each tied to its own client. A repeated init() replaces the client, so the old handlers kept flushing the replaced client and added an extra waitUntil to every response. Surfaced by the Sentry review on #25125.

The listeners are now registered once and flush whichever client is current. A replaced client is no longer flushed on SIGTERM. That matches docs/repeated-init.md (repeated init() is unsupported, and buffered data stays on the old client), and it keeps the 500ms SIGTERM window for the active client.

@chargome chargome self-assigned this Oct 7, 2026
@chargome

chargome commented Oct 7, 2026

Copy link
Copy Markdown
Member Author

bugbot run

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 2c481c7. Configure here.

@chargome
chargome marked this pull request as ready for review October 7, 2026 16:43
@chargome
chargome requested a review from a team as a code owner October 7, 2026 16:43
@chargome
chargome requested review from JPeer264, andreiborza and isaacs and removed request for a team October 7, 2026 16:43
@github-actions

github-actions Bot commented Oct 8, 2026 •

Copy link
Copy Markdown
Contributor

size-limit report 📦

Path Size % Change Change
@sentry/browser 29.73 kB - -
@sentry/browser - with treeshaking flags 27.85 kB - -
@sentry/browser - with treeshaking flags tracing without tracing 27.76 kB - -
@sentry/browser (incl. Tracing) 51.77 kB - -
@sentry/browser (incl. Tracing + Span Streaming) 51.79 kB - -
@sentry/browser (incl. Tracing, Profiling) 54.72 kB - -
@sentry/browser (incl. Tracing, Replay) 91.47 kB - -
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags 80.35 kB - -
@sentry/browser (incl. Tracing, Replay with Canvas) 96.18 kB - -
@sentry/browser (incl. Tracing, Replay, Feedback) 109.16 kB - -
@sentry/browser (incl. Feedback) 47.25 kB - -
@sentry/browser (incl. sendFeedback) 34.77 kB - -
@sentry/browser (incl. FeedbackAsync) 39.88 kB - -
@sentry/browser (incl. Metrics) 30.75 kB - -
@sentry/browser (incl. Logs) 31.03 kB - -
@sentry/browser (incl. Metrics & Logs) 31.67 kB - -
@sentry/react 31.55 kB - -
@sentry/react (incl. Tracing) 54.1 kB - -
@sentry/vue 37.76 kB - -
@sentry/vue (incl. Tracing) 54.7 kB - -
@sentry/svelte 29.75 kB - -
@sentry/remix (Remix 3 client bundle) 56.76 kB - -
CDN Bundle 31.44 kB - -
CDN Bundle (incl. Tracing) 52.28 kB - -
CDN Bundle (incl. Logs, Metrics) 33.65 kB - -
CDN Bundle (incl. Tracing, Logs, Metrics) 54.23 kB - -
CDN Bundle (incl. Replay, Logs, Metrics) 74.55 kB - -
CDN Bundle (incl. Tracing, Replay) 89.92 kB - -
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) 91.87 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback) 96.08 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) 98.06 kB - -
CDN Bundle - uncompressed 92.73 kB - -
CDN Bundle (incl. Tracing) - uncompressed 155.26 kB - -
CDN Bundle (incl. Logs, Metrics) - uncompressed 99.26 kB - -
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed 161.22 kB - -
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed 229.23 kB - -
CDN Bundle (incl. Tracing, Replay) - uncompressed 275.36 kB - -
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed 281.3 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed 289.06 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed 294.99 kB - -
@sentry/nextjs (client) 56.46 kB - -
@sentry/sveltekit (client) 52.16 kB - -
@sentry/core/server 40.86 kB - -
@sentry/core/browser 13.71 kB - -
@sentry/node 145.89 kB +0.03% +35 B 🔺
@sentry/node/import (ESM hook with diagnostics-channel injection) 83.33 kB - -
@sentry/node - without tracing 93.69 kB +0.04% +33 B 🔺
@sentry/node - without channel injection 124.04 kB +0.03% +37 B 🔺
@sentry/aws-serverless 101.91 kB +0.03% +23 B 🔺
@sentry/cloudflare (withSentry) - minified 209.71 kB - -
@sentry/cloudflare (withSentry) 519.94 kB - -

View base workflow run

A repeated init() registered another SIGTERM handler and response-finish
subscriber per client, so replaced clients kept being flushed and every
response got an extra waitUntil. Register the listeners once and flush the
current client.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@chargome
chargome force-pushed the fix/vercel-keepalive-repeated-init branch from 88a9c2d to 0ac8729 Compare October 8, 2026 08:44
The keep-alive listeners are registered once per module instance, so a
second init() on Vercel in the same test file no longer adds a SIGTERM
listener. Give each Vercel test a fresh module instance.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@chargome
chargome merged commit f1cdd11 into develop Oct 8, 2026
353 checks passed
@chargome
chargome deleted the fix/vercel-keepalive-repeated-init branch October 8, 2026 09:33
sentry-junior Bot added a commit to getsentry/toolkit that referenced this pull request Oct 28, 2026
Move the fix from a telemetry.ts workaround into the existing
@sentry/node-core patch. The light init() now registers one SIGTERM
listener that flushes the latest client, mirroring
getsentry/sentry-javascript#25146 for @sentry/node.
sentry-junior Bot added a commit to getsentry/toolkit that referenced this pull request Oct 28, 2026
Move the fix from a telemetry.ts workaround into the existing
@sentry/node-core patch. The light init() now registers one SIGTERM
listener that flushes the latest client, mirroring
getsentry/sentry-javascript#25146 for @sentry/node.
BYK added a commit to getsentry/toolkit that referenced this pull request Oct 28, 2026
When `VERCEL` is set, v10's `@sentry/node-core/light` `init()` adds a
`SIGTERM` flush listener on every init and never removes it.
Library-mode commands (`createSentrySDK`) call `initSentry()` per
command, so in-process callers like `@sentry/bundler-plugins` hit
`MaxListenersExceededWarning` during Vercel builds. v11 registers that
listener once (getsentry/sentry-javascript#25146), and the rest of
toolkit is already on v11, so the CLI now uses the workspace catalog
`@sentry/core` and `@sentry/node` (11.6.0). The local
`@sentry/node-core` and `@sentry/core` patches are gone.

**SDK defaults.** The CLI now uses v11 defaults rather than recreating
v10 behavior:
- CLI mode uses `Sentry.init()` defaults, including `dataCollection` and
`attachStacktrace`. The bundle didn't grow, because the bundled MCP code
already pulled in v11's integrations.
- The SDK's Http/NodeFetch integrations now create the `http.client`
spans and inject `sentry-trace`/`baggage`. The CLI's own HTTP spans and
manual header injection existed only because Bun's fetch didn't fire
undici diagnostics channels, and the CLI now runs on Node.
Response-cache hits are no longer wrapped in an `http.client` span, so
`span.op:http.client` counts only requests that went out. Cache
telemetry is unchanged: the response cache still emits
`cache.get`/`cache.put` spans and the `cache.hit_rate` metric. Retries
now show one span per attempt.
- Library mode uses `getDefaultIntegrationsWithoutPerformance()` and
leaves out integrations that hook the host process or its diagnostics
channels (process handlers, HTTP/fetch, child process, worker threads,
framework error handlers). Without those integrations, and on Bun (whose
fetch doesn't fire undici diagnostics channels), the API client still
creates its own `http.client` span and adds `sentry-trace`/`baggage`, so
in-process callers keep request tracing.
- Runtime channel injection is stripped at build time with the SDK's
`__SENTRY_CHANNEL_INJECTION__` define, which is what the bundler
plugins' `excludeChannelInjection` sets. That makes `index.cjs` ~72 KB
smaller. `enableRuntimeChannelInjection: false` stays so unbundled dev
and test runs behave the same.
- `unknown_command` and `invalid_argument` are sent with `captureEvent`
instead of `captureMessage`. With `attachStacktrace` on,
`captureMessage` adds a synthetic exception, which marks the session
errored.

**Kept on purpose:**
- `traceLifecycle: "static"`, because dashboards rely on scope tags on
transactions and `beforeSendTransaction` scrubs `server_name`; both are
dropped under v11's default span streaming. Moving to streamed spans
should be its own change.
- v11 removed `enableLogs`, so library mode drops logs via
`beforeSendLog` instead.

**Two v11 issues fixed here:**
- v11 attaches the client to the current scope *before* switching to the
AsyncLocalStorage context strategy. When `initSentry()` runs inside a
still-pending pre-init span, as it does in the CLI's middleware chain,
the client is lost, so commands like `sentry org list` sent no telemetry
at all. `initSentry()` now re-binds the client after init.
- `attachSentryReporter()` used a runtime `require()` that the bundler
leaves unresolved, so shipped builds either failed it silently or, in
library mode, could load the host app's `@sentry/node`. It's now a
static import, which means shipped builds now forward consola logs,
which they never did on v10.

Repeated `initSentry()` calls with `VERCEL=1` keep one `SIGTERM`
listener.

Fixes #1692

<!-- junior-session-footer:start -->
<!-- junior-conversation-id:slack%3AC010884QHLG%3A1793195293.030719 -->

--

[View Junior
Session](https://junior-prod.sentry.dev/conversations/slack%3AC010884QHLG%3A1793195293.030719)
[[Sentry]](https://sentry.sentry.io/explore/conversations/slack%3AC010884QHLG%3A1793195293.030719/?project=4510944073809921)

<!-- junior-session-footer:end -->

---------

Co-authored-by: sentry-junior[bot] <264270552+sentry-junior[bot]@users.noreply.github.com>
Co-authored-by: Burak Yigit Kaya <126780+BYK@users.noreply.github.com>
Co-authored-by: Burak Yigit Kaya <burak.kaya@sentry.io>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants