Skip to content

feat(node): Include global scope for eventLoopBlockIntegration#20108

Open
timfish wants to merge 1 commit intodevelopfrom
timfish/feat/event-loop-block-scope
Open

feat(node): Include global scope for eventLoopBlockIntegration#20108
timfish wants to merge 1 commit intodevelopfrom
timfish/feat/event-loop-block-scope

Conversation

@timfish
Copy link
Copy Markdown
Collaborator

@timfish timfish commented Apr 6, 2026

Global scope is not captured by the native module through the AsyncLocalStorage so this PR sends that via the polling mechanism.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 6, 2026

Semver Impact of This PR

🟡 Minor (new features)

📋 Changelog Preview

This is how your changes will appear in the changelog.
Entries from this PR are highlighted with a left border (blockquote style).


New Features ✨

Deps

  • Bump lodash.template from 4.5.0 to 4.18.1 by dependabot in #20085
  • Bump @xmldom/xmldom from 0.8.3 to 0.8.12 by dependabot in #20066

Other

  • (core) Support embeddings in langchain by nicohrubec in #20017
  • (core, node) Portable Express integration by isaacs in #19928
  • (deno) Add denoRuntimeMetricsIntegration by chargome in #20023
  • (node) Include global scope for eventLoopBlockIntegration by timfish in #20108
  • (node, bun) Enforce minimum collection interval in runtime metrics integrations by chargome in #20068

Bug Fixes 🐛

  • (aws-serverless) Add timeout to _endSpan forceFlush to prevent Lambda hanging by logaretm in #20064
  • (cloudflare) Ensure every request instruments functions by JPeer264 in #20044
  • (core) Set span.status to error when MCP tool returns JSON-RPC error response by betegon in #20082
  • (gatsby) Fix errorHandler signature to match bundler-plugin-core API by JPeer264 in #20048

Internal Changes 🔧

Core

  • Do not emit spans for chats.create in google-genai by nicohrubec in #19990
  • Unify .do* span ops to gen_ai.generate_content by nicohrubec in #20074
  • Simplify addResponseAttributes in openai integration by nicohrubec in #20013
  • Extract shared endStreamSpan for AI integrations by nicohrubec in #20021
  • Remove provider-specific AI span attributes in favor of gen_ai attributes in sentry conventions by nicohrubec in #20011

Deps

  • Bump mshick/add-pr-comment from dd126dd8c253650d181ad9538d8b4fa218fc31e8 to e7516d74559b5514092f5b096ed29a629a1237c6 by dependabot in #20078
  • Bump getsentry/craft/.github/workflows/changelog-preview.yml from 2.24.1 to 2.25.2 by dependabot in #20081

Other

  • (node) Add node integration tests for Vercel ToolLoopAgent by nicohrubec in #20087
  • Update validate-pr workflow by stephanie-anderson in #20072
  • Remove unused tsconfig-template folder by mydea in #20067

🤖 This preview updates automatically when you update the PR.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 6, 2026

size-limit report 📦

⚠️ Warning: Base artifact is not the latest one, because the latest workflow run is not done yet. This may lead to incorrect results. Try to re-run all tests to get up to date results.

Path Size % Change Change
@sentry/browser 25.65 kB +0.02% +5 B 🔺
@sentry/browser - with treeshaking flags 24.14 kB +0.03% +5 B 🔺
@sentry/browser (incl. Tracing) 42.16 kB +0.02% +7 B 🔺
@sentry/browser (incl. Tracing, Profiling) 46.77 kB +0.02% +9 B 🔺
@sentry/browser (incl. Tracing, Replay) 80.94 kB +0.01% +5 B 🔺
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags 70.56 kB +0.01% +5 B 🔺
@sentry/browser (incl. Tracing, Replay with Canvas) 85.66 kB +0.01% +8 B 🔺
@sentry/browser (incl. Tracing, Replay, Feedback) 97.92 kB +0.01% +5 B 🔺
@sentry/browser (incl. Feedback) 42.42 kB +0.02% +6 B 🔺
@sentry/browser (incl. sendFeedback) 30.31 kB +0.02% +6 B 🔺
@sentry/browser (incl. FeedbackAsync) 35.3 kB +0.05% +17 B 🔺
@sentry/browser (incl. Metrics) 26.96 kB +0.03% +7 B 🔺
@sentry/browser (incl. Logs) 27.11 kB +0.03% +7 B 🔺
@sentry/browser (incl. Metrics & Logs) 27.78 kB +0.03% +7 B 🔺
@sentry/react 27.41 kB +0.03% +6 B 🔺
@sentry/react (incl. Tracing) 44.48 kB +0.02% +5 B 🔺
@sentry/vue 30.08 kB +0.02% +5 B 🔺
@sentry/vue (incl. Tracing) 44.05 kB +0.02% +8 B 🔺
@sentry/svelte 25.67 kB +0.02% +5 B 🔺
CDN Bundle 28.33 kB +0.05% +14 B 🔺
CDN Bundle (incl. Tracing) 43.13 kB +0.06% +25 B 🔺
CDN Bundle (incl. Logs, Metrics) 29.7 kB +0.05% +14 B 🔺
CDN Bundle (incl. Tracing, Logs, Metrics) 44.17 kB +0.05% +19 B 🔺
CDN Bundle (incl. Replay, Logs, Metrics) 68.5 kB +0.03% +17 B 🔺
CDN Bundle (incl. Tracing, Replay) 80.02 kB +0.03% +19 B 🔺
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) 81.06 kB +0.03% +18 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback) 85.56 kB +0.02% +15 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) 86.59 kB +0.02% +16 B 🔺
CDN Bundle - uncompressed 82.72 kB +0.07% +54 B 🔺
CDN Bundle (incl. Tracing) - uncompressed 127.86 kB +0.05% +54 B 🔺
CDN Bundle (incl. Logs, Metrics) - uncompressed 86.86 kB +0.07% +54 B 🔺
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed 131.27 kB +0.05% +54 B 🔺
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed 209.84 kB +0.03% +54 B 🔺
CDN Bundle (incl. Tracing, Replay) - uncompressed 244.74 kB +0.03% +54 B 🔺
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed 248.13 kB +0.03% +54 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed 257.65 kB +0.03% +54 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed 261.04 kB +0.03% +54 B 🔺
@sentry/nextjs (client) 46.9 kB +0.02% +7 B 🔺
@sentry/sveltekit (client) 42.62 kB +0.02% +7 B 🔺
@sentry/node-core 55.77 kB +0.04% +17 B 🔺
@sentry/node 172.41 kB -0.2% -338 B 🔽
@sentry/node - without tracing 96.04 kB +0.03% +26 B 🔺
@sentry/aws-serverless 112.85 kB +0.07% +78 B 🔺

View base workflow run

@timfish timfish changed the title feat: Include global scope for eventLoopBlockIntegration feat(node): Include global scope for eventLoopBlockIntegration Apr 6, 2026
@timfish timfish marked this pull request as ready for review April 7, 2026 07:54
Comment on lines +51 to +53
const globalScope = getGlobalScope().getScopeData();
const currentScope = getCurrentScope().getScopeData();
mergeScopeData(globalScope, currentScope);
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Is this required of can we just sync the global scope?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

this seems reasonable to me? ideally I guess it should be global + isolation + current scope, not sure if/how isolation scope is handled here generally :)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Isolation scope is captured via native code from the AsyncLocalStorage!

@timfish timfish requested a review from Lms24 April 7, 2026 07:55
Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: ScopeData with non-serializable fields passed to native module
    • Added getSerializableScopeData() function to strip non-serializable fields (eventProcessors, span, attachments) from ScopeData before passing to threadPoll() for cross-thread serialization.

Create PR

Or push these changes by commenting:

@cursor push 24ad4e1753
Preview (24ad4e1753)
diff --git a/packages/node-native/src/common.ts b/packages/node-native/src/common.ts
--- a/packages/node-native/src/common.ts
+++ b/packages/node-native/src/common.ts
@@ -40,6 +40,6 @@
 
 export interface ThreadState {
   session: Session | undefined;
-  scope: ScopeData;
+  scope: Partial<ScopeData>;
   debugImages: Record<string, string>;
 }

diff --git a/packages/node-native/src/event-loop-block-integration.ts b/packages/node-native/src/event-loop-block-integration.ts
--- a/packages/node-native/src/event-loop-block-integration.ts
+++ b/packages/node-native/src/event-loop-block-integration.ts
@@ -54,6 +54,24 @@
   return globalScope;
 }
 
+/**
+ * Extracts only the serializable fields from ScopeData that can be passed to the native module.
+ * Removes non-serializable fields like eventProcessors (functions), span (complex object), and attachments.
+ */
+function getSerializableScopeData(scopeData: ScopeData): Partial<ScopeData> {
+  return {
+    tags: scopeData.tags,
+    attributes: scopeData.attributes,
+    extra: scopeData.extra,
+    user: scopeData.user,
+    contexts: scopeData.contexts,
+    level: scopeData.level,
+    fingerprint: scopeData.fingerprint,
+    propagationContext: scopeData.propagationContext,
+    conversationId: scopeData.conversationId,
+  };
+}
+
 type IntegrationInternal = { start: () => void; stop: () => void };
 
 function poll(enabled: boolean, clientOptions: ClientOptions): void {
@@ -62,7 +80,9 @@
     // We need to copy the session object and remove the toJSON method so it can be sent to the worker
     // serialized without making it a SerializedSession
     const session = currentSession ? { ...currentSession, toJSON: undefined } : undefined;
-    const scope = getLocalScopeData();
+    const scopeData = getLocalScopeData();
+    // Strip non-serializable fields from scope data before passing to native module
+    const scope = getSerializableScopeData(scopeData);
     // message the worker to tell it the main event loop is still running
     threadPoll(enabled, { session, scope, debugImages: getFilenameToDebugIdMap(clientOptions.stackParser) });
   } catch {

This Bugbot Autofix run was free. To enable autofix for future PRs, go to the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 0742247. Configure here.

const currentScope = getCurrentScope().getScopeData();
mergeScopeData(globalScope, currentScope);
return globalScope;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

ScopeData with non-serializable fields passed to native module

Medium Severity

getLocalScopeData() returns a full ScopeData object containing eventProcessors (an array of functions) and potentially span (a complex object with methods). This is passed to threadPoll which needs to share data cross-thread via the native module. The existing code already carefully strips the non-serializable toJSON method from session objects before passing them to threadPoll, but no similar care is taken for ScopeData. If event processors are registered on the global or current scope, the non-serializable functions could cause the threadPoll call to fail, with the error silently swallowed by the try/catch, losing all scope data.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 0742247. Configure here.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

ScopeData doesn't contain any of those and is safe to serialise...

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.

How to include user and tags in events from eventLoopBlockIntegration

2 participants