From 81d0def53adf7bdda249577d67298b706c06ac04 Mon Sep 17 00:00:00 2001 From: Nicolas Hrubec Date: Wed, 18 Feb 2026 11:49:22 +0100 Subject: [PATCH 1/7] fix(core): Langgraph state graph invoke accepts null to resume --- .../suites/tracing/langgraph/scenario.mjs | 4 +++ .../suites/tracing/langgraph/test.ts | 28 +++++++++++++++++++ packages/core/src/tracing/langgraph/index.ts | 2 +- 3 files changed, 33 insertions(+), 1 deletion(-) diff --git a/dev-packages/node-integration-tests/suites/tracing/langgraph/scenario.mjs b/dev-packages/node-integration-tests/suites/tracing/langgraph/scenario.mjs index d93c4b5491c7..b9e5555247e6 100644 --- a/dev-packages/node-integration-tests/suites/tracing/langgraph/scenario.mjs +++ b/dev-packages/node-integration-tests/suites/tracing/langgraph/scenario.mjs @@ -44,6 +44,10 @@ async function run() { { role: 'user', content: 'Tell me about the weather' }, ], }); + + // Test: invoke with null input (resume after human-in-the-loop interrupt) + // see: https://docs.langchain.com/oss/javascript/langgraph/use-functional-api#resuming-after-an-error + await graph.invoke(null); }); await Sentry.flush(2000); diff --git a/dev-packages/node-integration-tests/suites/tracing/langgraph/test.ts b/dev-packages/node-integration-tests/suites/tracing/langgraph/test.ts index 5905d592ee7a..e4365abc96c9 100644 --- a/dev-packages/node-integration-tests/suites/tracing/langgraph/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/langgraph/test.ts @@ -67,6 +67,20 @@ describe('LangGraph integration', () => { origin: 'auto.ai.langgraph', status: 'ok', }), + // Third invoke_agent span with null input (resume scenario) + expect.objectContaining({ + data: expect.objectContaining({ + [GEN_AI_OPERATION_NAME_ATTRIBUTE]: 'invoke_agent', + [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'gen_ai.invoke_agent', + [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ai.langgraph', + [GEN_AI_AGENT_NAME_ATTRIBUTE]: 'weather_assistant', + [GEN_AI_PIPELINE_NAME_ATTRIBUTE]: 'weather_assistant', + }), + description: 'invoke_agent weather_assistant', + op: 'gen_ai.invoke_agent', + origin: 'auto.ai.langgraph', + status: 'ok', + }), ]), }; @@ -116,6 +130,20 @@ describe('LangGraph integration', () => { origin: 'auto.ai.langgraph', status: 'ok', }), + // Third invoke_agent span with null input (resume scenario) + expect.objectContaining({ + data: expect.objectContaining({ + [GEN_AI_OPERATION_NAME_ATTRIBUTE]: 'invoke_agent', + [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'gen_ai.invoke_agent', + [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ai.langgraph', + [GEN_AI_AGENT_NAME_ATTRIBUTE]: 'weather_assistant', + [GEN_AI_PIPELINE_NAME_ATTRIBUTE]: 'weather_assistant', + }), + description: 'invoke_agent weather_assistant', + op: 'gen_ai.invoke_agent', + origin: 'auto.ai.langgraph', + status: 'ok', + }), ]), }; diff --git a/packages/core/src/tracing/langgraph/index.ts b/packages/core/src/tracing/langgraph/index.ts index 6a9c39a7ddda..c1e838bd1914 100644 --- a/packages/core/src/tracing/langgraph/index.ts +++ b/packages/core/src/tracing/langgraph/index.ts @@ -136,7 +136,7 @@ function instrumentCompiledGraphInvoke( const recordInputs = options.recordInputs; const recordOutputs = options.recordOutputs; const inputMessages = - args.length > 0 ? ((args[0] as { messages?: LangChainMessage[] }).messages ?? []) : []; + args.length > 0 ? ((args[0] as { messages?: LangChainMessage[] } | null)?.messages ?? []) : []; if (inputMessages && recordInputs) { const normalizedMessages = normalizeLangChainMessages(inputMessages); From 472d2d3c888d971513625dd57540b72ca9a5a617 Mon Sep 17 00:00:00 2001 From: Nicolas Hrubec Date: Wed, 18 Feb 2026 13:17:00 +0100 Subject: [PATCH 2/7] update --- .../tracing/langgraph/scenario-resume.mjs | 43 ++++++++++ .../suites/tracing/langgraph/scenario.mjs | 3 - .../suites/tracing/langgraph/test.ts | 78 ++++++++++++------- 3 files changed, 93 insertions(+), 31 deletions(-) create mode 100644 dev-packages/node-integration-tests/suites/tracing/langgraph/scenario-resume.mjs diff --git a/dev-packages/node-integration-tests/suites/tracing/langgraph/scenario-resume.mjs b/dev-packages/node-integration-tests/suites/tracing/langgraph/scenario-resume.mjs new file mode 100644 index 000000000000..f30416c3fd0d --- /dev/null +++ b/dev-packages/node-integration-tests/suites/tracing/langgraph/scenario-resume.mjs @@ -0,0 +1,43 @@ +import { END, MemorySaver, MessagesAnnotation, START, StateGraph } from '@langchain/langgraph'; +import * as Sentry from '@sentry/node'; + +async function run() { + await Sentry.startSpan({ op: 'function', name: 'langgraph-resume-test' }, async () => { + const mockLlm = () => { + return { + messages: [ + { + role: 'assistant', + content: 'Mock LLM response', + response_metadata: { + model_name: 'mock-model', + finish_reason: 'stop', + tokenUsage: { + promptTokens: 20, + completionTokens: 10, + totalTokens: 30, + }, + }, + }, + ], + }; + }; + + // Test: invoke with null input (resume after human-in-the-loop interrupt) + // See: https://github.com/getsentry/sentry-javascript/issues/19353 + const checkpointer = new MemorySaver(); + const graph = new StateGraph(MessagesAnnotation) + .addNode('agent', mockLlm) + .addEdge(START, 'agent') + .addEdge('agent', END) + .compile({ name: 'resume_agent', checkpointer }); + + const config = { configurable: { thread_id: 'resume-thread-1' } }; + await graph.invoke({ messages: [{ role: 'user', content: 'Hello' }] }, config); + await graph.invoke(null, config); + }); + + await Sentry.flush(2000); +} + +run(); diff --git a/dev-packages/node-integration-tests/suites/tracing/langgraph/scenario.mjs b/dev-packages/node-integration-tests/suites/tracing/langgraph/scenario.mjs index b9e5555247e6..0df37c406b75 100644 --- a/dev-packages/node-integration-tests/suites/tracing/langgraph/scenario.mjs +++ b/dev-packages/node-integration-tests/suites/tracing/langgraph/scenario.mjs @@ -45,9 +45,6 @@ async function run() { ], }); - // Test: invoke with null input (resume after human-in-the-loop interrupt) - // see: https://docs.langchain.com/oss/javascript/langgraph/use-functional-api#resuming-after-an-error - await graph.invoke(null); }); await Sentry.flush(2000); diff --git a/dev-packages/node-integration-tests/suites/tracing/langgraph/test.ts b/dev-packages/node-integration-tests/suites/tracing/langgraph/test.ts index e4365abc96c9..d30e1221f713 100644 --- a/dev-packages/node-integration-tests/suites/tracing/langgraph/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/langgraph/test.ts @@ -67,20 +67,6 @@ describe('LangGraph integration', () => { origin: 'auto.ai.langgraph', status: 'ok', }), - // Third invoke_agent span with null input (resume scenario) - expect.objectContaining({ - data: expect.objectContaining({ - [GEN_AI_OPERATION_NAME_ATTRIBUTE]: 'invoke_agent', - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'gen_ai.invoke_agent', - [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ai.langgraph', - [GEN_AI_AGENT_NAME_ATTRIBUTE]: 'weather_assistant', - [GEN_AI_PIPELINE_NAME_ATTRIBUTE]: 'weather_assistant', - }), - description: 'invoke_agent weather_assistant', - op: 'gen_ai.invoke_agent', - origin: 'auto.ai.langgraph', - status: 'ok', - }), ]), }; @@ -130,20 +116,6 @@ describe('LangGraph integration', () => { origin: 'auto.ai.langgraph', status: 'ok', }), - // Third invoke_agent span with null input (resume scenario) - expect.objectContaining({ - data: expect.objectContaining({ - [GEN_AI_OPERATION_NAME_ATTRIBUTE]: 'invoke_agent', - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'gen_ai.invoke_agent', - [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ai.langgraph', - [GEN_AI_AGENT_NAME_ATTRIBUTE]: 'weather_assistant', - [GEN_AI_PIPELINE_NAME_ATTRIBUTE]: 'weather_assistant', - }), - description: 'invoke_agent weather_assistant', - op: 'gen_ai.invoke_agent', - origin: 'auto.ai.langgraph', - status: 'ok', - }), ]), }; @@ -346,4 +318,54 @@ describe('LangGraph integration', () => { }); }, ); + + // Test for null input resume scenario (https://github.com/getsentry/sentry-javascript/issues/19353) + const EXPECTED_TRANSACTION_RESUME = { + transaction: 'langgraph-resume-test', + contexts: { + trace: expect.objectContaining({ + status: 'ok', + }), + }, + spans: expect.arrayContaining([ + // create_agent span + expect.objectContaining({ + data: { + [GEN_AI_OPERATION_NAME_ATTRIBUTE]: 'create_agent', + [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'gen_ai.create_agent', + [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ai.langgraph', + [GEN_AI_AGENT_NAME_ATTRIBUTE]: 'resume_agent', + }, + description: 'create_agent resume_agent', + op: 'gen_ai.create_agent', + origin: 'auto.ai.langgraph', + status: 'ok', + }), + // invoke_agent span with null input (resume) + expect.objectContaining({ + data: expect.objectContaining({ + [GEN_AI_OPERATION_NAME_ATTRIBUTE]: 'invoke_agent', + [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'gen_ai.invoke_agent', + [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ai.langgraph', + [GEN_AI_AGENT_NAME_ATTRIBUTE]: 'resume_agent', + [GEN_AI_PIPELINE_NAME_ATTRIBUTE]: 'resume_agent', + [GEN_AI_CONVERSATION_ID_ATTRIBUTE]: 'resume-thread-1', + }), + description: 'invoke_agent resume_agent', + op: 'gen_ai.invoke_agent', + origin: 'auto.ai.langgraph', + status: 'ok', + }), + ]), + }; + + createEsmAndCjsTests(__dirname, 'scenario-resume.mjs', 'instrument.mjs', (createRunner, test) => { + test('should not throw when invoke is called with null input (resume scenario)', async () => { + await createRunner() + .ignore('event') + .expect({ transaction: EXPECTED_TRANSACTION_RESUME }) + .start() + .completed(); + }); + }); }); From 8f80b5d36b617823ed4d988b40008bd18fdea240 Mon Sep 17 00:00:00 2001 From: Nicolas Hrubec Date: Wed, 18 Feb 2026 13:17:54 +0100 Subject: [PATCH 3/7] remove empty space --- .../node-integration-tests/suites/tracing/langgraph/scenario.mjs | 1 - 1 file changed, 1 deletion(-) diff --git a/dev-packages/node-integration-tests/suites/tracing/langgraph/scenario.mjs b/dev-packages/node-integration-tests/suites/tracing/langgraph/scenario.mjs index 0df37c406b75..d93c4b5491c7 100644 --- a/dev-packages/node-integration-tests/suites/tracing/langgraph/scenario.mjs +++ b/dev-packages/node-integration-tests/suites/tracing/langgraph/scenario.mjs @@ -44,7 +44,6 @@ async function run() { { role: 'user', content: 'Tell me about the weather' }, ], }); - }); await Sentry.flush(2000); From 1cd5e87b204e7ecf155363a7bc7b330cefce937e Mon Sep 17 00:00:00 2001 From: Nicolas Hrubec Date: Wed, 18 Feb 2026 13:18:55 +0100 Subject: [PATCH 4/7] update comment --- .../suites/tracing/langgraph/scenario-resume.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev-packages/node-integration-tests/suites/tracing/langgraph/scenario-resume.mjs b/dev-packages/node-integration-tests/suites/tracing/langgraph/scenario-resume.mjs index f30416c3fd0d..a908f642e5ae 100644 --- a/dev-packages/node-integration-tests/suites/tracing/langgraph/scenario-resume.mjs +++ b/dev-packages/node-integration-tests/suites/tracing/langgraph/scenario-resume.mjs @@ -24,7 +24,7 @@ async function run() { }; // Test: invoke with null input (resume after human-in-the-loop interrupt) - // See: https://github.com/getsentry/sentry-javascript/issues/19353 + // See: https://docs.langchain.com/oss/javascript/langgraph/use-functional-api#resuming-after-an-error const checkpointer = new MemorySaver(); const graph = new StateGraph(MessagesAnnotation) .addNode('agent', mockLlm) From b2f50d297087c12ba4e0eef9d66728501b07638b Mon Sep 17 00:00:00 2001 From: Nicolas Hrubec Date: Wed, 18 Feb 2026 13:32:28 +0100 Subject: [PATCH 5/7] . --- .../node-integration-tests/suites/tracing/langgraph/test.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/dev-packages/node-integration-tests/suites/tracing/langgraph/test.ts b/dev-packages/node-integration-tests/suites/tracing/langgraph/test.ts index d30e1221f713..89cd583da89b 100644 --- a/dev-packages/node-integration-tests/suites/tracing/langgraph/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/langgraph/test.ts @@ -361,11 +361,7 @@ describe('LangGraph integration', () => { createEsmAndCjsTests(__dirname, 'scenario-resume.mjs', 'instrument.mjs', (createRunner, test) => { test('should not throw when invoke is called with null input (resume scenario)', async () => { - await createRunner() - .ignore('event') - .expect({ transaction: EXPECTED_TRANSACTION_RESUME }) - .start() - .completed(); + await createRunner().ignore('event').expect({ transaction: EXPECTED_TRANSACTION_RESUME }).start().completed(); }); }); }); From f984951d62d62ec2efe40f153adaa206a7e35e84 Mon Sep 17 00:00:00 2001 From: Nicolas Hrubec Date: Thu, 19 Feb 2026 14:48:13 +0100 Subject: [PATCH 6/7] add contributor to changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9831e22335ae..0b9135fedac4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,7 +36,7 @@ This new mode no longer creates a session per soft navigation but continues the initial session until the next hard page refresh. Check out the [docs](https://docs.sentry.io/platforms/javascript/guides/nextjs/configuration/integrations/browsersession/) to learn more! -Work in this release was contributed by @LudvigHz. Thank you for your contribution! +Work in this release was contributed by @LudvigHz and @jadengis. Thank you for your contribution! ## 10.39.0 From 770d9df276a9b9d9386bbea89b6cced09f62f1e3 Mon Sep 17 00:00:00 2001 From: Nicolas Hrubec Date: Thu, 19 Feb 2026 14:50:51 +0100 Subject: [PATCH 7/7] Update changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0b9135fedac4..06f161870c58 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,7 +36,7 @@ This new mode no longer creates a session per soft navigation but continues the initial session until the next hard page refresh. Check out the [docs](https://docs.sentry.io/platforms/javascript/guides/nextjs/configuration/integrations/browsersession/) to learn more! -Work in this release was contributed by @LudvigHz and @jadengis. Thank you for your contribution! +Work in this release was contributed by @LudvigHz and @jadengis. Thank you for your contributions! ## 10.39.0