Skip to content

ref(node-core): Move node fetch instrumentation into node-core#21873

Merged
mydea merged 3 commits into
fn/node-fetch-enhance-undicifrom
fn/node-fetch-to-node-core
Jul 1, 2026
Merged

ref(node-core): Move node fetch instrumentation into node-core#21873
mydea merged 3 commits into
fn/node-fetch-enhance-undicifrom
fn/node-fetch-to-node-core

Conversation

@mydea

@mydea mydea commented Jul 1, 2026

Copy link
Copy Markdown
Member

Stacked on #21872.

Moves the undici-instrumentation (spans + breadcrumbs + trace propagation) and the vendored node-fetch types from the node package into node-core, and exposes instrumentUndici / NodeFetchOptions from @sentry/node-core.

What changes:

  • node-core's nativeNodeFetchIntegration now instruments via instrumentUndici directly (spans default to true).
  • The node package keeps a thin nativeNodeFetchIntegration wrapper (node-fetch.ts) that derives the spans default from the client options (skipOpenTelemetrySetup / hasSpansEnabled) before delegating to instrumentUndici. In v11 this will become the only implementation.
  • SentryNodeFetchInstrumentation is now unused internally and is marked @deprecated. It stays exported for backwards compatibility and will be removed in a future major version.

Review the first PR in the stack (#21872) first — this PR is purely the relocation.

🤖 Generated with Claude Code

Move the `undici-instrumentation` (spans + breadcrumbs + trace propagation) and the vendored
`node-fetch` types from the `node` package into `node-core`, and expose `instrumentUndici` /
`NodeFetchOptions` from `@sentry/node-core`.

`node-core`'s `nativeNodeFetchIntegration` now instruments via `instrumentUndici` directly
(spans default to `true`). The `node` package keeps a thin `nativeNodeFetchIntegration` wrapper
(`node-fetch.ts`) that derives the `spans` default from the client options
(`skipOpenTelemetrySetup` / `hasSpansEnabled`) before delegating to `instrumentUndici`.

`SentryNodeFetchInstrumentation` is now unused internally and is marked `@deprecated`; it stays
exported for backwards compatibility and will be removed in a future major version.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@mydea mydea requested a review from a team as a code owner July 1, 2026 08:23
@mydea mydea requested review from JPeer264 and andreiborza and removed request for a team July 1, 2026 08:23
Comment thread packages/node-core/src/integrations/node-fetch/index.ts
SentryNodeFetchInstrumentation,
type SentryNodeFetchInstrumentationOptions,
} from './integrations/node-fetch/SentryNodeFetchInstrumentation';
export { addFetchRequestBreadcrumb, addTracePropagationHeadersToFetchRequest } from './utils/outgoingFetchRequest';

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Removed public fetch helper exports

Medium Severity

This change drops addFetchRequestBreadcrumb and addTracePropagationHeadersToFetchRequest from the @sentry/node-core package entry without a deprecation period, which is a breaking public API removal for anyone importing those helpers from the main export.

Fix in Cursor Fix in Web

Triggered by project rule: PR Review Guidelines for Cursor Bot

Reviewed by Cursor Bugbot for commit d4e9512. 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 was just added in the base-stack PR so should be fine

@mydea mydea marked this pull request as draft July 1, 2026 08:28

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

There are 2 total unresolved issues (including 1 from previous review).

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 db92a29. Configure here.

Comment thread packages/node-core/src/integrations/node-fetch/undici-instrumentation.ts Outdated
@mydea mydea marked this pull request as ready for review July 1, 2026 08:33
// When spans are disabled we do not create a span, but we still inject trace propagation headers
// directly (unless disabled via `tracePropagation`).
if (config.spans === false) {
if (!config.spans) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Bug: The check !config.spans incorrectly disables span creation when the spans option is undefined, which is the default for nativeNodeFetchIntegration().
Severity: HIGH

Suggested Fix

Revert the condition at line 193 from !config.spans to config.spans === false. This will correctly handle the undefined case as not explicitly disabled, aligning with the documented default behavior of creating spans.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent. Verify if this is a real issue. If it is, propose a fix; if not, explain why it's
not valid.

Location: packages/node-core/src/integrations/node-fetch/undici-instrumentation.ts#L193

Potential issue: The condition to check if spans should be created for `undici`
instrumentation was changed from `config.spans === false` to `!config.spans`. When
`nativeNodeFetchIntegration()` is called without options, as is common in packages like
`@sentry/bun` and `@sentry/aws-serverless`, the `spans` option is `undefined`. The new
check `!undefined` evaluates to `true`, causing the function to return early and skip
the creation of fetch spans. This contradicts the documentation, which states that spans
are enabled by default, and will break distributed tracing for outgoing HTTP requests in
multiple packages.

Also affects:

  • packages/bun/src/sdk.ts:43
  • packages/node-core/src/sdk/index.ts:63
  • dev-packages/e2e-tests/test-applications/node-express/src/app.ts:18

Did we get this right? 👍 / 👎 to inform future reviews.

@JPeer264 JPeer264 Jul 1, 2026

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.

If I'm not mistaken it has nothing to do with the nativeNodeFetchIntegration, the function just has the same name

Edit: Ok, I just checked (I just got confused with another with the same name) - this shouldn't be a problem, as spans should always be set by the in the node-fetch.ts and it will always be a boolean.

But I do wonder if there are differences now between nativeNodeFetchIntegration from node-core/light - but I think it is the plan to get rid of that if I understand #21778 correctly

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.

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.

yeah, this is on purpose and is this way to remain the behavior we have today - which is that in node-core we do not emit spans for this by default. So in node, we default this to true (same as before) generally speaking, and in node-core it is always default false, but users can now opt-in to it if they want (theoretically)

@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

size-limit report 📦

Path Size % Change Change
@sentry/browser 27.62 kB - -
@sentry/browser - with treeshaking flags 26.05 kB - -
@sentry/browser (incl. Tracing) 46.07 kB - -
@sentry/browser (incl. Tracing + Span Streaming) 47.82 kB - -
@sentry/browser (incl. Tracing, Profiling) 50.84 kB - -
@sentry/browser (incl. Tracing, Replay) 85.31 kB - -
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags 74.91 kB - -
@sentry/browser (incl. Tracing, Replay with Canvas) 89.99 kB - -
@sentry/browser (incl. Tracing, Replay, Feedback) 102.67 kB - -
@sentry/browser (incl. Feedback) 44.8 kB - -
@sentry/browser (incl. sendFeedback) 32.42 kB - -
@sentry/browser (incl. FeedbackAsync) 37.55 kB - -
@sentry/browser (incl. Metrics) 28.68 kB - -
@sentry/browser (incl. Logs) 28.93 kB - -
@sentry/browser (incl. Metrics & Logs) 29.61 kB - -
@sentry/react 29.41 kB - -
@sentry/react (incl. Tracing) 48.38 kB - -
@sentry/vue 32.85 kB - -
@sentry/vue (incl. Tracing) 47.93 kB - -
@sentry/svelte 27.64 kB - -
CDN Bundle 30.02 kB - -
CDN Bundle (incl. Tracing) 48.02 kB - -
CDN Bundle (incl. Logs, Metrics) 31.58 kB - -
CDN Bundle (incl. Tracing, Logs, Metrics) 49.35 kB - -
CDN Bundle (incl. Replay, Logs, Metrics) 70.79 kB - -
CDN Bundle (incl. Tracing, Replay) 85.51 kB - -
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) 86.79 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback) 91.32 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) 92.56 kB - -
CDN Bundle - uncompressed 89.42 kB - -
CDN Bundle (incl. Tracing) - uncompressed 145.35 kB - -
CDN Bundle (incl. Logs, Metrics) - uncompressed 94.12 kB - -
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed 149.32 kB - -
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed 218.66 kB - -
CDN Bundle (incl. Tracing, Replay) - uncompressed 264.36 kB - -
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed 268.32 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed 278.06 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed 282.01 kB - -
@sentry/nextjs (client) 50.76 kB - -
@sentry/sveltekit (client) 46.46 kB - -
@sentry/core/server 77.75 kB - -
@sentry/core/browser 64.06 kB - -
@sentry/node-core 62.37 kB +1.47% +901 B 🔺
@sentry/node 121.5 kB -0.47% -570 B 🔽
@sentry/node/import (ESM hook with diagnostics-channel injection) 69.95 kB - -
@sentry/node/light 50.46 kB +0.03% +11 B 🔺
@sentry/node - without tracing 72.68 kB -0.72% -527 B 🔽
@sentry/aws-serverless 83.52 kB -0.69% -575 B 🔽
@sentry/cloudflare (withSentry) - minified 180.62 kB - -
@sentry/cloudflare (withSentry) 446.93 kB - -

View base workflow run

const clientOptions = getClient()?.getOptions();
instrumentUndici({
...options,
spans: _shouldInstrumentSpans(options, clientOptions),

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.

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.

yes, in normal node setup we want to generally enable this, while in node-core it should always be opt-in to maintain the current behavior (where spans are never emitted)

@mydea mydea merged commit 03f75c5 into develop Jul 1, 2026
206 checks passed
@mydea mydea deleted the fn/node-fetch-to-node-core branch July 1, 2026 11:08
mydea added a commit that referenced this pull request Jul 1, 2026
…mentation (#21872)

Enhances the node `undici-instrumentation` into a single instrumentation
that emits `http.client` spans, records breadcrumbs, and propagates
traces for outgoing fetch/undici requests. Previously this was split
across two subscribers on the same diagnostics channels:
`instrumentUndici` (spans) and `SentryNodeFetchInstrumentation`
(breadcrumbs + trace propagation).

What changes:
- Span creation is gated by a `spans` option (default `true`). When
spans are disabled, breadcrumbs are still recorded and trace propagation
headers are still injected (gated by `tracePropagation`).
- Trace propagation now goes through the dedup-aware
`addTracePropagationHeadersToFetchRequest`, which gains an optional
`span` argument so outgoing headers reference the `http.client` span
while still de-duplicating against trace headers the user set manually
(e.g. via `getTraceData()`). This preserves the double-baggage
prevention that the two-instrumentation setup previously provided.
- `@sentry/node-core` now exports `addFetchRequestBreadcrumb` and
`addTracePropagationHeadersToFetchRequest` so the node instrumentation
can reuse them.

_Root cause:_ folding the two subscribers into one dropped the de-dup
pass that ran after span-based header injection; routing propagation
through `addTracePropagationHeadersToFetchRequest` (now span-aware)
restores it.

This PR is intentionally scoped to the `node` package (plus the minimal
`node-core` support above) to keep the diff reviewable. A stacked
follow-up (#21873) moves the instrumentation into `node-core`.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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