Skip to content

fix(node): Avoid duplicated sentry-trace and baggage headers on fetch requests#19960

Draft
Lms24 wants to merge 9 commits intodevelopfrom
lms/fix-double-baggage-propagation
Draft

fix(node): Avoid duplicated sentry-trace and baggage headers on fetch requests#19960
Lms24 wants to merge 9 commits intodevelopfrom
lms/fix-double-baggage-propagation

Conversation

@Lms24
Copy link
Member

@Lms24 Lms24 commented Mar 24, 2026

This PR fixes a bunch of issues within our node fetch integration for tracing header edge cases.

  • mergeBaggageHeaders would mix up sentry- entries from both headers creating completely inconsistent baggage entries. We must not mix up entries as otherwise the propagated DSC values would be inconsistent. This would interfere with dynamic sampling.
  • addTracePropagationHeadersToFetchRequest would correctly avoid setting a sentry-trace header if a previous one existed but happily still add new baggage and optionally traceparent headers. Again causing inconsistent data between sentry-trace and the other two values (e.g. distinct trace ids across the headers in one request). I'd argue that as soon as a sentry-trace header is found, we should no longer add any new headers.
  • manually adding baggage headers with sentry- entries would cause multiple baggage headers to be present. This was due to insufficient deduplication in our own header adding logic. What makes this much worse though is that we cannot stop OTel's UndiciInstrumentation from injecting headers.

Open issue: If users manually add a baggage header, we have to deal with 3 competing entries:

  1. user-added
  2. OTel Undici-added
  3. Sentry-added (in our own fetch instrumentation)

Right now, this PR correctly dedupes BUT prefers 2 over 1 and 3. There's a case to be made that 1 should be preferred. Also according to a comment in the code, 3 should be preferred over 2. Still need to fix that. Therefore setting this to draft.

closes #19158

@github-actions
Copy link
Contributor

github-actions bot commented Mar 24, 2026

Semver Impact of This PR

🟢 Patch (bug fixes)

📋 Changelog Preview

This is how your changes will appear in the changelog.
Entries from this PR are highlighted with a left border (blockquote style).


New Features ✨

  • (node) Add nodeRuntimeMetricsIntegration by chargome in #19923
  • (nuxt) Support parametrized SSR routes in Nuxt 5 by s1gr1d in #19977

Bug Fixes 🐛

  • (node) Avoid duplicated sentry-trace and baggage headers on fetch requests by Lms24 in #19960
  • (opentelemetry) Convert seconds timestamps in span.end() to milliseconds by logaretm in #19958

Documentation 📚

  • (release) Update publishing-a-release.md by nicohrubec in #19982

Internal Changes 🔧

  • (core) Consolidate getOperationName into one shared utility by nicohrubec in #19971
  • (deno) Expand Deno E2E test coverage by chargome in #19957

🤖 This preview updates automatically when you update the PR.

@Lms24 Lms24 marked this pull request as draft March 24, 2026 18:03
if (headerName === SENTRY_BAGGAGE_HEADER) {
// merge the initial entry into the later occurance so that we keep the initial sentry- values around.
// all other non-sentry values are merged
const merged = mergeBaggageHeaders(headers[i + 1] as string, headers[firstIndex + 1] as string);
Copy link

Choose a reason for hiding this comment

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

Bug: The mergeBaggageHeaders function is called with arguments in the wrong order, causing user-defined non-Sentry baggage values to be overwritten by automatically-appended OTel baggage.
Severity: HIGH

Suggested Fix

Swap the arguments in the mergeBaggageHeaders call within _deduplicateArrayHeader. The initial, user-set header (headers[firstIndex + 1]) should be the first argument, and the later, OTel-appended header (headers[i + 1]) should be the second. This will ensure that the user's non-Sentry baggage values are preserved.

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/utils/outgoingFetchRequest.ts#L154

Potential issue: In `_deduplicateArrayHeader`, the call to `mergeBaggageHeaders`
incorrectly prioritizes non-Sentry values from later baggage headers. The function is
called with the OTel-appended header as the first argument (`existing`) and the
user-defined header as the second (`new`). The merging logic only adds non-Sentry keys
from the `new` header if they don't already exist in the `existing` one. This causes
user-defined non-Sentry baggage values to be silently overwritten by values from the
automatically-appended OTel header, which contradicts the intended behavior of
preserving all user-set values.

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

@github-actions
Copy link
Contributor

github-actions bot commented Mar 24, 2026

size-limit report 📦

⚠️ Warning: Base artifact is not the latest one, because the latest workflow run is not done yet. This may lead to incorrect results. Try to re-run all tests to get up to date results.

Path Size % Change Change
@sentry/browser 25.69 kB +0.2% +49 B 🔺
@sentry/browser - with treeshaking flags 24.17 kB +0.14% +33 B 🔺
@sentry/browser (incl. Tracing) 42.67 kB +0.13% +54 B 🔺
@sentry/browser (incl. Tracing, Profiling) 47.33 kB +0.12% +55 B 🔺
@sentry/browser (incl. Tracing, Replay) 81.48 kB +0.08% +57 B 🔺
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags 71.06 kB +0.1% +69 B 🔺
@sentry/browser (incl. Tracing, Replay with Canvas) 86.17 kB +0.06% +50 B 🔺
@sentry/browser (incl. Tracing, Replay, Feedback) 98.41 kB +0.04% +36 B 🔺
@sentry/browser (incl. Feedback) 42.48 kB +0.08% +30 B 🔺
@sentry/browser (incl. sendFeedback) 30.35 kB +0.15% +44 B 🔺
@sentry/browser (incl. FeedbackAsync) 35.4 kB +0.12% +39 B 🔺
@sentry/browser (incl. Metrics) 26.96 kB +0.14% +37 B 🔺
@sentry/browser (incl. Logs) 27.1 kB +0.12% +32 B 🔺
@sentry/browser (incl. Metrics & Logs) 27.78 kB +0.15% +39 B 🔺
@sentry/react 27.45 kB +0.22% +58 B 🔺
@sentry/react (incl. Tracing) 45.01 kB +0.14% +60 B 🔺
@sentry/vue 30.13 kB +0.16% +46 B 🔺
@sentry/vue (incl. Tracing) 44.52 kB +0.09% +39 B 🔺
@sentry/svelte 25.7 kB +0.16% +40 B 🔺
CDN Bundle 28.35 kB +0.27% +75 B 🔺
CDN Bundle (incl. Tracing) 43.57 kB +0.15% +62 B 🔺
CDN Bundle (incl. Logs, Metrics) 29.22 kB +0.27% +77 B 🔺
CDN Bundle (incl. Tracing, Logs, Metrics) 44.43 kB +0.17% +75 B 🔺
CDN Bundle (incl. Replay, Logs, Metrics) 68.3 kB +0.13% +86 B 🔺
CDN Bundle (incl. Tracing, Replay) 80.41 kB +0.1% +73 B 🔺
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) 81.31 kB +0.1% +76 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback) 85.97 kB +0.12% +103 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) 86.85 kB +0.1% +85 B 🔺
CDN Bundle - uncompressed 82.7 kB +0.1% +77 B 🔺
CDN Bundle (incl. Tracing) - uncompressed 128.62 kB +0.05% +64 B 🔺
CDN Bundle (incl. Logs, Metrics) - uncompressed 85.57 kB +0.1% +77 B 🔺
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed 131.49 kB +0.05% +64 B 🔺
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed 209.22 kB +0.05% +102 B 🔺
CDN Bundle (incl. Tracing, Replay) - uncompressed 245.5 kB +0.04% +89 B 🔺
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed 248.35 kB +0.04% +89 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed 258.41 kB +0.04% +89 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed 261.26 kB +0.04% +89 B 🔺
@sentry/nextjs (client) 47.4 kB +0.08% +37 B 🔺
@sentry/sveltekit (client) 43.12 kB +0.12% +51 B 🔺
@sentry/node-core 56.75 kB +0.72% +401 B 🔺
@sentry/node 173.8 kB +0.38% +647 B 🔺
@sentry/node - without tracing 96.8 kB +0.48% +458 B 🔺
@sentry/aws-serverless 113.8 kB +0.41% +461 B 🔺

View base workflow run

@github-actions
Copy link
Contributor

github-actions bot commented Mar 24, 2026

node-overhead report 🧳

Note: This is a synthetic benchmark with a minimal express app and does not necessarily reflect the real-world performance impact in an application.

Scenario Requests/s % of Baseline Prev. Requests/s Change %
GET Baseline 9,224 - 9,043 +2%
GET With Sentry 1,483 16% 1,673 -11%
GET With Sentry (error only) 5,910 64% 6,094 -3%
POST Baseline 1,144 - 1,202 -5%
POST With Sentry 539 47% 588 -8%
POST With Sentry (error only) 998 87% 1,059 -6%
MYSQL Baseline 3,078 - 3,246 -5%
MYSQL With Sentry 365 12% 451 -19%
MYSQL With Sentry (error only) 2,500 81% 2,637 -5%

View base workflow run

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.

baggage is sent twice when tracesSampleRate is set and Sentry.getTraceData() is added manually

1 participant