Skip to content

Conversation

@RulaKhaled
Copy link
Member

This PR adds official support for instrumenting LangGraph StateGraph operations in Node with Sentry tracing, following OpenTelemetry semantic conventions for Generative AI.

Currently supported:

Node.js - Both agent creation and invocation are instrumented in this PR
ESM and CJS - Both module systems are supported

The langgraphIntegration() accepts the following options:

// The integration respects your sendDefaultPii client option
interface LangGraphOptions {
  recordInputs?: boolean;   // Whether to record input messages
  recordOutputs?: boolean;  // Whether to record response text and tool calls
}

e.g

Sentry.init({
  dsn: '__DSN__',
  sendDefaultPii: false, // Even with PII disabled globally
  integrations: [
    Sentry.langgraphIntegration({
      recordInputs: true,    // Force recording input messages
      recordOutputs: true,   // Force recording response text
    }),
  ],
});

Operations traced:

  • gen_ai.create_agent - Spans created when StateGraph.compile() is called
  • gen_ai.invoke_agent - Spans created when CompiledGraph.invoke() is called

@github-actions
Copy link
Contributor

github-actions bot commented Nov 6, 2025

size-limit report 📦

Path Size % Change Change
@sentry/browser 24.58 kB -0.07% -16 B 🔽
@sentry/browser - with treeshaking flags 23.07 kB -0.07% -16 B 🔽
@sentry/browser (incl. Tracing) 41.2 kB -0.06% -23 B 🔽
@sentry/browser (incl. Tracing, Profiling) 45.48 kB -0.06% -24 B 🔽
@sentry/browser (incl. Tracing, Replay) 79.68 kB -0.03% -17 B 🔽
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags 69.35 kB -0.04% -21 B 🔽
@sentry/browser (incl. Tracing, Replay with Canvas) 84.38 kB -0.02% -16 B 🔽
@sentry/browser (incl. Tracing, Replay, Feedback) 96.54 kB -0.03% -23 B 🔽
@sentry/browser (incl. Feedback) 41.25 kB -0.04% -16 B 🔽
@sentry/browser (incl. sendFeedback) 29.25 kB -0.07% -18 B 🔽
@sentry/browser (incl. FeedbackAsync) 34.18 kB -0.05% -17 B 🔽
@sentry/react 26.27 kB -0.08% -19 B 🔽
@sentry/react (incl. Tracing) 43.19 kB -0.06% -22 B 🔽
@sentry/vue 29.06 kB -0.07% -18 B 🔽
@sentry/vue (incl. Tracing) 42.97 kB -0.05% -19 B 🔽
@sentry/svelte 24.59 kB -0.07% -17 B 🔽
CDN Bundle 26.84 kB -0.17% -44 B 🔽
CDN Bundle (incl. Tracing) 41.75 kB -0.07% -28 B 🔽
CDN Bundle (incl. Tracing, Replay) 78.26 kB -0.05% -33 B 🔽
CDN Bundle (incl. Tracing, Replay, Feedback) 83.73 kB -0.05% -38 B 🔽
CDN Bundle - uncompressed 78.7 kB -0.18% -141 B 🔽
CDN Bundle (incl. Tracing) - uncompressed 123.82 kB -0.12% -142 B 🔽
CDN Bundle (incl. Tracing, Replay) - uncompressed 239.85 kB -0.06% -142 B 🔽
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed 252.62 kB -0.06% -142 B 🔽
@sentry/nextjs (client) 45.29 kB -0.04% -18 B 🔽
@sentry/sveltekit (client) 41.59 kB -0.05% -17 B 🔽
@sentry/node-core 50.75 kB -0.02% -6 B 🔽
@sentry/node 158.69 kB +0.56% +874 B 🔺
@sentry/node - without tracing 92.63 kB -0.01% -6 B 🔽
@sentry/aws-serverless 106.36 kB -0.01% -7 B 🔽

View base workflow run

@github-actions
Copy link
Contributor

github-actions bot commented Nov 6, 2025

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.
⚠️ 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.

Scenario Requests/s % of Baseline Prev. Requests/s Change %
GET Baseline 9,168 - 8,510 +8%
GET With Sentry 1,405 15% 1,274 +10%
GET With Sentry (error only) 6,061 66% 5,951 +2%
POST Baseline 1,208 - 1,153 +5%
POST With Sentry 523 43% 465 +12%
POST With Sentry (error only) 1,064 88% 1,011 +5%
MYSQL Baseline 3,358 - 3,245 +3%
MYSQL With Sentry 505 15% 407 +24%
MYSQL With Sentry (error only) 2,755 82% 2,655 +4%

View base workflow run

@RulaKhaled RulaKhaled marked this pull request as ready for review November 6, 2025 18:02
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: LANGGRAPH_ORIGIN,
[GEN_AI_OPERATION_NAME_ATTRIBUTE]: 'create_agent',
},
},
Copy link

Choose a reason for hiding this comment

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

Bug: Missing SEMANTIC_OP Attribute in Span Creation (Bugbot Rules)

Missing SEMANTIC_ATTRIBUTE_SENTRY_OP in span attributes. According to SDK conventions, when calling startSpan API, both SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN and SEMANTIC_ATTRIBUTE_SENTRY_OP must be set in the attributes object. While the 'op' field is set on the span options, the SEMANTIC_ATTRIBUTE_SENTRY_OP attribute is not included in the attributes object. This should be [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'gen_ai.create_agent' in the attributes.

Fix in Cursor Fix in Web

[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: LANGGRAPH_ORIGIN,
[GEN_AI_OPERATION_NAME_ATTRIBUTE]: 'invoke_agent',
},
},
Copy link

Choose a reason for hiding this comment

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

Bug: Missing SEMANTIC_ATTRIBUTE_SENTRY_OP in Span Attributes (Bugbot Rules)

Missing SEMANTIC_ATTRIBUTE_SENTRY_OP in span attributes. According to SDK conventions, when calling startSpan API, both SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN and SEMANTIC_ATTRIBUTE_SENTRY_OP must be set in the attributes object. While the 'op' field is set on the span options, the SEMANTIC_ATTRIBUTE_SENTRY_OP attribute is not included in the attributes object. This should be [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'gen_ai.invoke_agent' in the attributes.

Fix in Cursor Fix in Web

instrumentLangGraph(options);
},
};
}) satisfies IntegrationFn;
Copy link

Choose a reason for hiding this comment

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

Bug: Inconsistent Options Handling in LangGraph Integration (Bugbot Rules)

The integration object returned by _langgraphIntegration includes an options property, but LangChain integration (the equivalent pattern this is based on) does not include it. According to the codebase pattern, when using generateInstrumentOnce, the integration should NOT store options in the returned object since the options are passed directly to instrumentLangGraph(options) in the setupOnce method. The options property in the returned object is only used for integrations that don't use generateInstrumentOnce. This inconsistency could cause issues with integration setup and option handling. The options, line on line 14 should be removed to match the LangChain pattern.

Fix in Cursor Fix in Web

return result;
},
);
},
Copy link

Choose a reason for hiding this comment

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

Bug: Uncaught Errors in startSpan Invocation Handling (Bugbot Rules)

Missing error handling in startSpan call for instrumentCompiledGraphInvoke. According to review rules, when calling startSpan, error cases should be handled with try/catch and captureException. The async callback (line 95) can throw errors from Reflect.apply (line 121) but these are not caught. Similar integrations (e.g., OpenAI) wrap the original method call in try/catch and call captureException with appropriate mechanism details. This should wrap lines 121 in a try/catch block and call captureException with mechanism type 'auto.ai.langgraph.invoke' on errors.

Fix in Cursor Fix in Web


return compiledGraph;
},
);
Copy link

Choose a reason for hiding this comment

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

Bug: Unhandled errors in startSpan: wrap and report with captureException (Bugbot Rules)

Missing error handling in startSpan call for instrumentStateGraphCompile. According to review rules, when calling startSpan, error cases should be handled with try/catch and captureException. The callback (line 44) can throw errors from Reflect.apply (line 45) but these are not caught. Similar integrations (e.g., OpenAI) wrap the original method call in try/catch and call captureException with appropriate mechanism details. This should wrap line 45 in a try/catch block and call captureException with mechanism type 'auto.ai.langgraph.compile' on errors.

Fix in Cursor Fix in Web


if (inputMessages && recordInputs) {
const truncatedMessages = truncateGenAiMessages(inputMessages);
span.setAttribute(GEN_AI_REQUEST_MESSAGES_ATTRIBUTE, JSON.stringify(truncatedMessages));
Copy link

Choose a reason for hiding this comment

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

Bug: Normalize LangChain messages before truncation bug

The code passes raw LangChainMessage objects directly to truncateGenAiMessages without first normalizing them. The truncateGenAiMessages function expects messages in specific formats (with content: string or parts array), but LangChainMessage objects can have various structures (with _getType(), constructor names, serialized format, etc.). This should normalize messages first using normalizeLangChainMessages before truncating, similar to how the LangChain integration handles it in utils/langchain/utils.ts line 283. The correct approach would be:

const normalizedMessages = normalizeLangChainMessages(inputMessages);
const truncatedMessages = truncateGenAiMessages(normalizedMessages);

Fix in Cursor Fix in Web

@RulaKhaled RulaKhaled force-pushed the instrument-langgrapgh branch from 6b9a670 to e45920a Compare November 6, 2025 18:25
@RulaKhaled RulaKhaled closed this Nov 6, 2025
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.

2 participants