Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(node): ignore error rethrows in local variables integration #13480

Closed
wants to merge 2 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 22 additions & 2 deletions packages/node/src/integrations/local-variables/worker.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import type { Debugger, InspectorNotification, Runtime } from 'node:inspector';
import { Session } from 'node:inspector/promises';
import { parentPort, workerData } from 'node:worker_threads';
import type { StackParser } from '@sentry/types';
import type { StackFrame, StackParser } from '@sentry/types';
import { createStackParser, nodeStackLineParser } from '@sentry/utils';
import { createGetModuleFromFilename } from '../../utils/module';
import type { LocalVariablesWorkerArgs, PausedExceptionEvent, RateLimitIncrement, Variables } from './common';
import { createRateLimiter, hashFromStack } from './common';
import { createRateLimiter, functionNamesMatch, hashFromStack } from './common';

const options: LocalVariablesWorkerArgs = workerData;

Expand Down Expand Up @@ -104,9 +104,23 @@ async function handlePaused(
return;
}

const originalErrorStackFrames: StackFrame[] = stackParser(data.description).filter(
frame => frame.function !== 'new Promise',
);

// Debugger frames being longer than the originalErrorStackFrames means they definitely dont match
// Debugger callFrames don't include anything beyond an async boundary, so they should be SHORTER or equal to the full error stack
// If they are longer, we are in a re-throw and we should stop to avoid overriding the error's detected vars from the original throw
if (callFrames.length > originalErrorStackFrames.length) {
return;
}

const frames = [];

for (let i = 0; i < callFrames.length; i++) {
// sentry frames are in reverse order
const originalErrorStackFramesIndex = originalErrorStackFrames.length - 1 - i;

// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const { scopeChain, functionName, this: obj } = callFrames[i]!;

Expand All @@ -115,6 +129,12 @@ async function handlePaused(
// obj.className is undefined in ESM modules
const fn = obj.className === 'global' || !obj.className ? functionName : `${obj.className}.${functionName}`;

if (!functionNamesMatch(fn, originalErrorStackFrames[originalErrorStackFramesIndex]?.function)) {
// If at any point we encounter a function name that doesn't match the original error stack, we stop
// this means we are in a re-throw and we don't want to override the error's detected vars from the original throw
return;
}

if (localScope?.object.objectId === undefined) {
frames[i] = { function: fn };
} else {
Expand Down
Loading