From 28db5111dbb9f9d653318a240f15b42c51239d00 Mon Sep 17 00:00:00 2001 From: Tim Fish Date: Fri, 22 Mar 2024 13:03:38 +0000 Subject: [PATCH] fix(node): Local variables skipped after Promise (#11234) The debugger call stack does not include the `new Promise` frames that are parsed from `error.stack`. This means that when we go through the frames to apply the local variables, the frames don't match up and we bail. This patch ignores those frames when matching functions in the frames. --- .../LocalVariables/local-variables-caught.mjs | 14 ++++++++------ .../local-variables/local-variables-sync.ts | 16 +++++++++------- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/dev-packages/node-integration-tests/suites/public-api/LocalVariables/local-variables-caught.mjs b/dev-packages/node-integration-tests/suites/public-api/LocalVariables/local-variables-caught.mjs index a7427ac60157..8fec9dbb1dad 100644 --- a/dev-packages/node-integration-tests/suites/public-api/LocalVariables/local-variables-caught.mjs +++ b/dev-packages/node-integration-tests/suites/public-api/LocalVariables/local-variables-caught.mjs @@ -9,12 +9,14 @@ Sentry.init({ }); class Some { - two(name) { - throw new Error('Enough!'); + async two(name) { + return new Promise((_, reject) => { + reject(new Error('Enough!')); + }); } } -function one(name) { +async function one(name) { const arr = [1, '2', null]; const obj = { name, @@ -30,12 +32,12 @@ function one(name) { const ty = new Some(); - ty.two(name); + await ty.two(name); } -setTimeout(() => { +setTimeout(async () => { try { - one('some name'); + await one('some name'); } catch (e) { Sentry.captureException(e); } diff --git a/packages/node-experimental/src/integrations/local-variables/local-variables-sync.ts b/packages/node-experimental/src/integrations/local-variables/local-variables-sync.ts index 111dc6d36c1e..91fb9005b4c3 100644 --- a/packages/node-experimental/src/integrations/local-variables/local-variables-sync.ts +++ b/packages/node-experimental/src/integrations/local-variables/local-variables-sync.ts @@ -288,14 +288,16 @@ const _localVariablesSyncIntegration = (( return; } - const frameCount = exception.stacktrace?.frames?.length || 0; + // Filter out frames where the function name is `new Promise` since these are in the error.stack frames + // but do not appear in the debugger call frames + const frames = (exception.stacktrace?.frames || []).filter(frame => frame.function !== 'new Promise'); - for (let i = 0; i < frameCount; i++) { + for (let i = 0; i < frames.length; i++) { // Sentry frames are in reverse order - const frameIndex = frameCount - i - 1; + const frameIndex = frames.length - i - 1; // Drop out if we run out of frames to match up - if (!exception?.stacktrace?.frames?.[frameIndex] || !cachedFrame[i]) { + if (!frames[frameIndex] || !cachedFrame[i]) { break; } @@ -303,14 +305,14 @@ const _localVariablesSyncIntegration = (( // We need to have vars to add cachedFrame[i].vars === undefined || // We're not interested in frames that are not in_app because the vars are not relevant - exception.stacktrace.frames[frameIndex].in_app === false || + frames[frameIndex].in_app === false || // The function names need to match - !functionNamesMatch(exception.stacktrace.frames[frameIndex].function, cachedFrame[i].function) + !functionNamesMatch(frames[frameIndex].function, cachedFrame[i].function) ) { continue; } - exception.stacktrace.frames[frameIndex].vars = cachedFrame[i].vars; + frames[frameIndex].vars = cachedFrame[i].vars; } }