Is there an existing issue for this?
How do you use Sentry?
Sentry SaaS (sentry.io)
Which SDK are you using?
@sentry/browser (root cause is in @sentry/core's safeJoin, surfaced via the Breadcrumbs integration; observed through @sentry/nuxt)
SDK Version
10.56.0 (regression introduced in 10.55.0; last unaffected version: 10.54.0)
Framework Version
Nuxt 4 / Vue 3.5
Reproduction Example/SDK Setup
import * as Sentry from '@sentry/browser'
// Default integrations include Breadcrumbs with console capturing enabled
Sentry.init({ dsn: '__YOUR_DSN__' })
const el = document.createElement('div')
let getterInvoked = false
Object.defineProperty(el, 'id', {
get() {
getterInvoked = true // any side effect here now runs during console breadcrumb capture
return ''
},
})
console.log(el) // Sentry's console breadcrumb handler serializes the args here
queueMicrotask(() => console.warn('getter invoked by Sentry serialization?', getterInvoked))
// @sentry/core <= 10.54.0 -> false
// @sentry/core >= 10.55.0 -> true (incl. 10.56.0)
Steps to Reproduce
- Initialize any browser SDK with default integrations (Breadcrumbs
console: true).
console.log() an object that has a property getter (e.g. a DOM element, or any object with Object.defineProperty(..., { get })).
- Observe that the getter is executed synchronously as part of breadcrumb creation — even when DevTools is closed and no event is sent.
Expected Result
Capturing a console breadcrumb should be side-effect free: serializing the logged arguments for the breadcrumb message must not invoke user-defined property getters, nor traverse the DOM. (<= 10.54.0 behaved this way — non-primitive args were serialized with String(value).)
Actual Result
Getters are invoked and DOM elements are walked during breadcrumb serialization.
Root cause — @sentry/core safeJoin (packages/core/src/utils/string.ts), which the Breadcrumbs integration uses for the console breadcrumb message (message: safeJoin(handlerData.args, ' ') in _getConsoleBreadcrumbHandler):
// <= 10.54.0
- } else {
- output.push(String(value)); // "[object HTMLDivElement]" — no getters invoked
- }
// >= 10.55.0
+ } else if (value instanceof Error) {
+ output.push(value.message ? `${value.name}: ${value.message}` : value.name);
+ } else {
+ output.push(stringifyValue(void 0, value)); // <- invokes getters
+ }
For DOM elements, stringifyValue resolves to htmlTreeAsString → _htmlElementAsString (packages/core/src/utils/browser.ts), which reads elem.id / elem.className / elem.getAttribute(...) and walks the ancestor chain — executing getters and adding DOM-traversal cost to every console.log(element).
Additional Context
- Real-world impact (how we found it): an anti-DevTools library uses the classic trap
Object.defineProperty(div, 'id', { get() { /* assume DevTools open */ } }) followed by console.log(div), expecting the getter to fire only when DevTools renders the object. After upgrading @sentry/nuxt 10.53.1 → 10.55.0, Sentry's breadcrumb serialization began invoking that id getter on every page, causing a false "DevTools open" detection (and a forced redirect) with DevTools closed. We confirmed via a pristine (un-instrumented) iframe console.log that the getter is read only through the main-window Sentry-instrumented console.
- Precedent that this class of bug is known/dangerous:
safeJoin already special-cases isVueViewModel to avoid serialization side effects (the infinite console-warning loop fixed in fix(utils): Prevent iterating over VueViewModel #8981). Invoking arbitrary getters / DOM traversal is the same category of risk (side effects, performance, potential exceptions).
- Affected range:
10.55.0 … 10.56.0 (latest). 10.54.0 and earlier are unaffected.
- Suggestion: keep breadcrumb-message serialization side-effect-free for non-primitive args (e.g. retain
String(value) for the human-readable message, since the structured data.arguments already carries the raw args for later normalization), or guard stringifyValue/htmlTreeAsString against invoking getters during breadcrumb capture.
Priority
React with 👍 to help prioritize this issue.
Is there an existing issue for this?
safeJoin/stringifyValue/ console-breadcrumb getter side effects)10.56.0)How do you use Sentry?
Sentry SaaS (sentry.io)
Which SDK are you using?
@sentry/browser(root cause is in@sentry/core'ssafeJoin, surfaced via the Breadcrumbs integration; observed through@sentry/nuxt)SDK Version
10.56.0 (regression introduced in 10.55.0; last unaffected version: 10.54.0)
Framework Version
Nuxt 4 / Vue 3.5
Reproduction Example/SDK Setup
Steps to Reproduce
console: true).console.log()an object that has a property getter (e.g. a DOM element, or any object withObject.defineProperty(..., { get })).Expected Result
Capturing a console breadcrumb should be side-effect free: serializing the logged arguments for the breadcrumb
messagemust not invoke user-defined property getters, nor traverse the DOM. (<= 10.54.0behaved this way — non-primitive args were serialized withString(value).)Actual Result
Getters are invoked and DOM elements are walked during breadcrumb serialization.
Root cause —
@sentry/coresafeJoin(packages/core/src/utils/string.ts), which the Breadcrumbs integration uses for the console breadcrumbmessage(message: safeJoin(handlerData.args, ' ')in_getConsoleBreadcrumbHandler):For DOM elements,
stringifyValueresolves tohtmlTreeAsString→_htmlElementAsString(packages/core/src/utils/browser.ts), which readselem.id/elem.className/elem.getAttribute(...)and walks the ancestor chain — executing getters and adding DOM-traversal cost to everyconsole.log(element).Additional Context
Object.defineProperty(div, 'id', { get() { /* assume DevTools open */ } })followed byconsole.log(div), expecting the getter to fire only when DevTools renders the object. After upgrading@sentry/nuxt10.53.1 → 10.55.0, Sentry's breadcrumb serialization began invoking thatidgetter on every page, causing a false "DevTools open" detection (and a forced redirect) with DevTools closed. We confirmed via a pristine (un-instrumented) iframeconsole.logthat the getter is read only through the main-window Sentry-instrumented console.safeJoinalready special-casesisVueViewModelto avoid serialization side effects (the infinite console-warning loop fixed in fix(utils): Prevent iterating over VueViewModel #8981). Invoking arbitrary getters / DOM traversal is the same category of risk (side effects, performance, potential exceptions).10.55.0…10.56.0(latest).10.54.0and earlier are unaffected.String(value)for the human-readablemessage, since the structureddata.argumentsalready carries the raw args for later normalization), or guardstringifyValue/htmlTreeAsStringagainst invoking getters during breadcrumb capture.Priority
React with 👍 to help prioritize this issue.