You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
FunctionToString overflows on cyclic Proxy prototype chains #25963
The patched Function.prototype.toString from functionToStringIntegration throws RangeError: Maximum call stack size exceeded for a function whose prototype chain loops back through a Proxy. Native toString returns normally for the same function. Anti-tamper and fingerprinting scripts use this kind of probe to detect patched natives.
Reported by Sergio Lombana from a customer on @sentry/react@10.75.3 with Castle bot detection loaded. Their event has a short stack: the Sentry toString wrapper is the top frame and the frames below it are Castle's.
Root cause (high confidence for the repro, unverified for the customer's exact trigger)
The wrapper calls getOriginalFunction(this), which reads this.__sentry_original__ (packages/core/src/utils/object.ts). For a function without that own property, the read walks the prototype chain. If the chain loops back through a Proxy, the walk never ends and overflows inside the wrapper frame. Native toString never reads properties from this, so it doesn't overflow.
This still happens on develop after #25921 / #25922. Those PRs fixed the apply re-entry and huge-argument-list overflows, not this property lookup.
Reproduction
constorig=Function.prototype.toString;constcallOrig=Function.prototype.call.bind(orig);functionf(){}Object.setPrototypeOf(f,Object.create(newProxy(f,{})));Function.prototype.toString.call(f);// native: 'function f() {}'// same shape as the current wrapper on developFunction.prototype.toString=function(){consto=this.__sentry_original__;returncallOrig(o??this);};Function.prototype.toString.call(f);// RangeError: Maximum call stack size exceeded
Unknowns
Someone else reproduced this artificial case but couldn't trigger the error with the same Castle and SDK versions. So it's not confirmed that this is the probe Castle runs in the customer's environment.
Workaround
Filter the RangeError via ignoreErrors / beforeSend. Removing functionToStringIntegration can make Sentry-wrapped functions look tampered to anti-bot scripts, so it isn't recommended here.
I ran into this too and have a small fix ready: the toString wrapper reads __sentry_original__ with a normal property lookup, which walks the looping Proxy prototype chain. Checking it's an own property first (which is how markFunctionWrapped sets it) and moving the read inside the existing try/catch fixes it, with a regression test using your repro. Happy to open a PR if that works for you.
I'd like to take this on. I'd guard the original-function lookup in the toString wrapper and fall back to the bound native call when that lookup throws. That would avoid adding a live .call lookup or changing how inherited wrappers are resolved. Is that approach welcome?
Activity
linear-code commentedon Oct 30, 2026
JS-4049
thelastbackspace commentedon Nov 4, 2026
I ran into this too and have a small fix ready: the toString wrapper reads
__sentry_original__with a normal property lookup, which walks the looping Proxy prototype chain. Checking it's an own property first (which is howmarkFunctionWrappedsets it) and moving the read inside the existing try/catch fixes it, with a regression test using your repro. Happy to open a PR if that works for you.adityareddy-dev commentedon Nov 4, 2026
I'd like to take this on. I'd guard the original-function lookup in the toString wrapper and fall back to the bound native call when that lookup throws. That would avoid adding a live .call lookup or changing how inherited wrappers are resolved. Is that approach welcome?