Skip to content

feat(bun): Add bunHttpServerIntegration - #22870

Open
mydea wants to merge 6 commits into
developfrom
fn/bun-http-integration
Open

feat(bun): Add bunHttpServerIntegration#22870
mydea wants to merge 6 commits into
developfrom
fn/bun-http-integration

Conversation

@mydea

@mydea mydea commented Jul 30, 2026

Copy link
Copy Markdown
Member

Adds a bunHttpIntegration to @sentry/bun that isolates incoming requests for servers built on node:http when running under the Bun runtime.

Problem

Bun does not emit the http.server.request.start diagnostics channel that the Node SDK relies on (getHttpServerSubscriptions in @sentry/core) to isolate each incoming request. As a result, servers built on node:http — notably Next.js running via bun --bun — do not get a fresh isolation scope and propagation context per request. Two unrelated incoming requests can therefore end up sharing a single trace_id.

This was surfaced by the nextjs-16-bun e2e app: propagation.test.ts › Does not propagate outgoing fetch requests not covered by tracePropagationTargets failed because the inbound and outbound transactions shared one trace.

Approach

bunHttpServerIntegration patches http.Server.prototype.emit and, on each server's first 'request' event, hands that server to the exact same core instrumentation the Node SDK uses (getHttpServerSubscriptionsinstrumentServer). Core then installs its per-instance emit wrapper, which shadows the prototype patch for all subsequent requests to that server. No changes to @sentry/core, @sentry/opentelemetry, or the tracer — this only reuses existing core logic at a Bun-specific entry point.

Two decisions worth calling out:

  • Prototype patch, not createServer. The server is typically created and listen()ed before Sentry initializes. Next.js in particular creates its http.Server and calls listen() before running the instrumentation.ts register() hook that loads the Sentry config, so a createServer patch installs too late to see the already-created server. Patching Server.prototype.emit catches pre-existing servers on their next request.
  • spans option. When another layer already emits incoming-request spans (Next.js emits its own OpenTelemetry http.server spans), pass spans: false so the integration only isolates the request and resets its trace, without creating duplicate transactions. This mirrors how the Node httpIntegration uses disableIncomingRequestSpans.

The integration is a no-op outside Bun (guarded on process.versions.bun), and is added by default when using the bun sdk.

e2e wiring

The nextjs-16-bun test app uses @sentry/nextjs (→ @sentry/node), so it does not pick this up automatically. @sentry/bun is added as a dependency of that app and bunHttpIntegration({ spans: false }) is added to its server config.

Known limitation

Servers created via an ES module namespace import (import * as http from 'node:http') are covered, since we patch the shared http.Server class rather than a module binding.

@mydea
mydea force-pushed the fn/bun-http-integration branch from 7bc2c44 to aa5fc6a Compare July 30, 2026 12:53
@mydea mydea changed the title feat(bun): Add bunHttpIntegration for node:http request isolation feat(bun): Add bunHttpServerIntegration Jul 30, 2026
@mydea
mydea requested a review from isaacs July 30, 2026 14:18

@isaacs isaacs left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Only real concern that I saw is the missing errorMonitor symbol, and the other settings nits. (Posting as a comment rather than "request changes" because I'm going to be away next week and don't want to block landing once addressed.)

Comment thread packages/bun/test/integrations/bunHttpServer.test.ts Outdated

const { [HTTP_ON_SERVER_REQUEST]: onServerRequest } = getHttpServerSubscriptions({
spans: options.spans ?? true,
sessions: options.sessions ?? true,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

It's interesting that Deno uses this same interface but sets sessions: false. I got confused at first and thought this was a bug, but actually it looks like Deno is the one that's wrong, and this is correct. Posted in #22888.

Comment thread packages/bun/src/integrations/bunHttpServer.ts Outdated
Comment thread packages/bun/src/integrations/bunHttpServer.ts Outdated
Comment thread packages/bun/src/integrations/bunHttpServer.ts
Comment thread packages/bun/src/integrations/bunHttpServer.ts Outdated
@mydea
mydea marked this pull request as ready for review July 31, 2026 07:07
@mydea
mydea requested a review from a team as a code owner July 31, 2026 07:08
@mydea
mydea requested review from JPeer264 and isaacs and removed request for a team July 31, 2026 07:08
mydea and others added 6 commits July 31, 2026 12:09
Bun does not emit the `http.server.request.start` diagnostics channel that the
Node SDK relies on to isolate each incoming request. As a result, servers built
on `node:http` (such as Next.js running via `bun --bun`) do not get a fresh
isolation scope and trace per request, so unrelated requests can share one trace.

`bunHttpIntegration` closes this gap by patching `http.Server.prototype.emit` and,
on each server's first `request` event, handing the server to the same core
instrumentation the Node SDK uses (`getHttpServerSubscriptions` → `instrumentServer`).
We patch the prototype rather than `createServer` because the server is typically
created and `listen()`ed before Sentry initializes (e.g. Next.js creates its server
before running the `instrumentation.ts` `register()` hook).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds an assertion that an `http.server` span is created for incoming requests
when spans are enabled, alongside the existing isolation and trace-continuation
coverage.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: isaacs <i@izs.me>
@mydea
mydea force-pushed the fn/bun-http-integration branch from 2c2d1bd to 5c0580d Compare July 31, 2026 10:09

@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.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 5c0580d. Configure here.

/**
* Do not capture the request body for incoming HTTP requests to URLs where the given callback returns `true`.
*/
ignoreRequestBody?: (url: string, request: http.RequestOptions) => boolean;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Wrong ignoreRequestBody request type

Medium Severity

ignoreRequestBody is typed to receive http.RequestOptions, but core invokes that callback with an incoming HttpIncomingMessage. Sibling options in the same interface already use HttpIncomingMessage, and Deno's equivalent integration types this correctly.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 5c0580d. Configure here.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

this is fine, we do the same in node

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.

2 participants