Skip to content

Conversation

@chargome
Copy link
Member

Some users get flooded by third party errors despite having set their third party error filtering to "drop-error-if-contains-third-party-frames". This comes from the fact that often the stacktrace includes our internal wrapper logic as last trace in the stack.

With this PR we're trying to work around this by specifically ignoring these frames with a new opt-in mechanism. Marked this as experimental, so users will know this option might lead to errors being misclassified.

  • Adds a new experimental option experimentalExcludeSentryInternalFrames to the thirdPartyErrorFilterIntegration
  • Once enabled we apply a strict filter for frames to detect our internal wrapping logic and filter them out so they do not misclassify injected code as internal errors.

Closes #13835

@chargome chargome self-assigned this Dec 29, 2025
@linear
Copy link

linear bot commented Dec 29, 2025

@chargome chargome changed the title feat(core): Add experimental option for excluding sentry internal frames for thirdPartyErrorFilterIntegration feat(core): Add experimentalExcludeSentryInternalFrames option to thirdPartyErrorFilterIntegration Dec 29, 2025
Comment on lines +117 to +144
function isSentryInternalFrame(frame: StackFrame, frameIndex: number): boolean {
// Only match the last frame (index 0 in reversed stack)
if (frameIndex !== 0 || !frame.context_line || !frame.filename) {
return false;
}

// Filename would look something like this: 'node_modules/@sentry/browser/build/npm/esm/helpers.js'
if (!frame.filename.includes('sentry') || !frame.filename.includes('helpers')) {
return false;
}

// Must have context_line with the exact fn.apply pattern (case-sensitive)
if (!frame.context_line.includes(SENTRY_INTERNAL_FN_APPLY)) {
return false;
}

// Check pre_context array for comment pattern
if (frame.pre_context) {
const len = frame.pre_context.length;
for (let i = 0; i < len; i++) {
if (frame.pre_context[i]?.includes(SENTRY_INTERNAL_COMMENT)) {
return true;
}
}
}

return false;
}
Copy link
Member Author

Choose a reason for hiding this comment

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

I know that this filter is overly strict, but I'd like to avoid false positives at all costs here. Open for suggestions on how to make this smarter though.

@chargome chargome requested review from Lms24 and timfish December 29, 2025 15:13
Comment on lines +117 to +119
function isSentryInternalFrame(frame: StackFrame, frameIndex: number): boolean {
// Only match the last frame (index 0 in reversed stack)
if (frameIndex !== 0 || !frame.context_line || !frame.filename) {
Copy link

Choose a reason for hiding this comment

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

Bug: The isSentryInternalFrame function incorrectly checks for frameIndex !== 0. It should identify the last frame in the stack, not the first, to correctly filter Sentry internal frames.
Severity: CRITICAL | Confidence: High

🔍 Detailed Analysis

The isSentryInternalFrame function incorrectly checks if a frame is at index 0 (frameIndex !== 0) to identify a Sentry internal frame. This logic assumes the internal frame is always the first frame in the stack. However, the feature's goal is to filter the last frame, which is Sentry's internal wrapper. Because the stack trace is not reversed, this check is incorrect. As a result, the experimentalExcludeSentryInternalFrames option will fail to filter out Sentry's internal frames when they appear at their expected position at the end of the stack trace, defeating the purpose of the feature.

💡 Suggested Fix

Modify the condition in isSentryInternalFrame to correctly identify the last frame of the stack trace. Instead of checking frameIndex !== 0, the check should be against the total length of the frames array, for example, frameIndex === frames.length - 1.

🤖 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/core/src/integrations/third-party-errors-filter.ts#L117-L119

Potential issue: The `isSentryInternalFrame` function incorrectly checks if a frame is
at index 0 (`frameIndex !== 0`) to identify a Sentry internal frame. This logic assumes
the internal frame is always the first frame in the stack. However, the feature's goal
is to filter the *last* frame, which is Sentry's internal wrapper. Because the stack
trace is not reversed, this check is incorrect. As a result, the
`experimentalExcludeSentryInternalFrames` option will fail to filter out Sentry's
internal frames when they appear at their expected position at the end of the stack
trace, defeating the purpose of the feature.

Did we get this right? 👍 / 👎 to inform future reviews.
Reference ID: 7994207

Copy link
Member Author

Choose a reason for hiding this comment

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

It is checking the reversed stack

@github-actions
Copy link
Contributor

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 11,586 - 9,285 +25%
GET With Sentry 1,996 17% 1,723 +16%
GET With Sentry (error only) 7,685 66% 6,019 +28%
POST Baseline 1,179 - 1,213 -3%
POST With Sentry 591 50% 601 -2%
POST With Sentry (error only) 1,020 87% 1,068 -4%
MYSQL Baseline 3,925 - 3,356 +17%
MYSQL With Sentry 529 13% 459 +15%
MYSQL With Sentry (error only) 3,209 82% 2,714 +18%

View base workflow run

Comment on lines +119 to +131
if (frameIndex !== 0 || !frame.context_line || !frame.filename) {
return false;
}

// Filename would look something like this: 'node_modules/@sentry/browser/build/npm/esm/helpers.js'
if (!frame.filename.includes('sentry') || !frame.filename.includes('helpers')) {
return false;
}

// Must have context_line with the exact fn.apply pattern (case-sensitive)
if (!frame.context_line.includes(SENTRY_INTERNAL_FN_APPLY)) {
return false;
}
Copy link
Member

Choose a reason for hiding this comment

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

l: can we combine these cases to save some bytes?

* If set to true, the integration will exclude frames that are internal to the Sentry SDK from the third-party frame detection.
* Note that enabling this option might lead to errors being misclassified as third-party errors.
*/
experimentalExcludeSentryInternalFrames?: boolean;
Copy link
Member

Choose a reason for hiding this comment

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

I'm wondering if we need the experimental prefix here at all. My thinking is that it'll cause us more bytes in the long run than to introduce the option directly. If the user who reported this is willing to test the changes, we could cut an experimental release of the SDK, let them test first and then release it. WDYT?

* If set to true, the integration will exclude frames that are internal to the Sentry SDK from the third-party frame detection.
* Note that enabling this option might lead to errors being misclassified as third-party errors.
*/
experimentalExcludeSentryInternalFrames?: boolean;
Copy link
Member

Choose a reason for hiding this comment

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

l/nit (feel free to disgredard): Should we rather call this

Suggested change
experimentalExcludeSentryInternalFrames?: boolean;
experimentalIgnoreSentryInternalFrames?: boolean;

? IIUC we want to avoid making decisions based on these frames so we ignore them. But really up to you, I'm fine with either.

@Lms24
Copy link
Member

Lms24 commented Dec 29, 2025

It makes sense to not alter existing behaviour in the major but if this proves to be valuable, perhaps it should become default behaviour in the next major?

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.

thirdPartyErrorFilterIntegration does not filter errors correctly

4 participants