Errors thrown in tools are ignored in streamEvents (using ToolNode and DynamicStructuredTool) #386

Open
opened 2026-02-15 18:16:23 -05:00 by yindo · 0 comments
Owner

Originally created by @danikenan on GitHub (Dec 20, 2025).

Checked other resources

  • I added a very descriptive title to this issue.
  • I searched the LangGraph.js documentation with the integrated search.
  • I used the GitHub search to find a similar question and didn't find it.
  • I am sure that this is a bug in LangGraph.js rather than my code.
  • The bug is not resolved by updating to the latest stable version of LangGraph (or the specific integration package).

Example Code

No events are fired in the event stream when a tool encounters an error.

when tool func is:

func: async () => {
    throw new Error('test error');
}

and ToolNode is:

new ToolNode([errorTool], { handleToolErrors: true }))
// OR
new ToolNode([errorTool], { handleToolErrors: false}))

These will never show up:

stream = graph.streamEvents(...)

for await (const event of stream) {
      if (event.event === 'on_tool_end') onToolEnd = true; // NOT called
      if (event.event === 'on_tool_error') onToolError = true; // NOT called
}

Fully working minimal sample:


import { AIMessage } from '@langchain/core/messages';
import { DynamicStructuredTool } from '@langchain/core/tools';
import { MessagesZodMeta, StateGraph, START, END, MemorySaver } from '@langchain/langgraph';
import { ToolNode } from '@langchain/langgraph/prebuilt';
import { registry } from '@langchain/langgraph/zod';
import { z } from 'zod';

const errorTool = new DynamicStructuredTool({
  name: 'error_tool',
  description: 'tool',
  schema: z.object({}),
  func: async () => {
    throw new Error('test error');
  },
});

const StateSchema = z.object({
  messages: z.array(z.custom<AIMessage>()).register(registry, MessagesZodMeta as never),
});

function createGraph(handleToolErrors: boolean) {
  return new StateGraph(StateSchema)
    .addNode('tools', new ToolNode([errorTool], { handleToolErrors }))
    .addEdge(START, 'tools')
    .addEdge('tools', END)
    .compile({ checkpointer: new MemorySaver() });
}

async function main() {
  for (const handleToolErrors of [true, false]) {
    const graph = createGraph(handleToolErrors);
    let onToolEnd = false;
    let onToolError = false;

    try {
      const stream = graph.streamEvents(
        {
          messages: [
            new AIMessage({
              tool_calls: [{ id: 'call_1', name: 'error_tool', args: {} }],
            }),
          ],
        },
        { version: 'v2' as const, configurable: { thread_id: `test-${handleToolErrors}` } }
      );

      for await (const event of stream) {
        if (event.event === 'on_tool_end') onToolEnd = true;
        if (event.event === 'on_tool_error') onToolError = true;
      }
    } catch {
      console.log('Error thrown expected when handleToolErrors=false', { handleToolErrors });
    }

    console.log(
      `handleToolErrors=${handleToolErrors}: on_tool_end=${onToolEnd}, on_tool_error=${onToolError}`
    );
  }
}

if (require.main === module) {
  main().catch(console.error);
}

Error Message and Stack Trace (if applicable)

No response

Description

  • I am using langgraph stream events to sync event with the client.
  • I expected to get notified that a tool fails (either with on_tool_end or on_tool_error)
  • No event is emitted, no matter the config of ToolNode or the Tool

System Info

└─┬ @langchain/langgraph-cli 1.1.2
└─┬ @langchain/langgraph-api 1.1.2
└─┬ @hono/zod-validator 0.2.2
└── ✕ unmet peer zod@^3.19.1: found 4.2.1 in @langchain/langgraph-api
Node version: v22.15.0
Operating system: darwin arm64
Package manager: pnpm
Package manager version: N/A

Originally created by @danikenan on GitHub (Dec 20, 2025). ### Checked other resources - [x] I added a very descriptive title to this issue. - [x] I searched the LangGraph.js documentation with the integrated search. - [x] I used the GitHub search to find a similar question and didn't find it. - [x] I am sure that this is a bug in LangGraph.js rather than my code. - [x] The bug is not resolved by updating to the latest stable version of LangGraph (or the specific integration package). ### Example Code No events are fired in the event stream when a tool encounters an error. when tool func is: ```typescript func: async () => { throw new Error('test error'); } ``` and ToolNode is: ```typescript new ToolNode([errorTool], { handleToolErrors: true })) // OR new ToolNode([errorTool], { handleToolErrors: false})) ``` These will never show up: ```typescript stream = graph.streamEvents(...) for await (const event of stream) { if (event.event === 'on_tool_end') onToolEnd = true; // NOT called if (event.event === 'on_tool_error') onToolError = true; // NOT called } ``` Fully working minimal sample: ```typescript import { AIMessage } from '@langchain/core/messages'; import { DynamicStructuredTool } from '@langchain/core/tools'; import { MessagesZodMeta, StateGraph, START, END, MemorySaver } from '@langchain/langgraph'; import { ToolNode } from '@langchain/langgraph/prebuilt'; import { registry } from '@langchain/langgraph/zod'; import { z } from 'zod'; const errorTool = new DynamicStructuredTool({ name: 'error_tool', description: 'tool', schema: z.object({}), func: async () => { throw new Error('test error'); }, }); const StateSchema = z.object({ messages: z.array(z.custom<AIMessage>()).register(registry, MessagesZodMeta as never), }); function createGraph(handleToolErrors: boolean) { return new StateGraph(StateSchema) .addNode('tools', new ToolNode([errorTool], { handleToolErrors })) .addEdge(START, 'tools') .addEdge('tools', END) .compile({ checkpointer: new MemorySaver() }); } async function main() { for (const handleToolErrors of [true, false]) { const graph = createGraph(handleToolErrors); let onToolEnd = false; let onToolError = false; try { const stream = graph.streamEvents( { messages: [ new AIMessage({ tool_calls: [{ id: 'call_1', name: 'error_tool', args: {} }], }), ], }, { version: 'v2' as const, configurable: { thread_id: `test-${handleToolErrors}` } } ); for await (const event of stream) { if (event.event === 'on_tool_end') onToolEnd = true; if (event.event === 'on_tool_error') onToolError = true; } } catch { console.log('Error thrown expected when handleToolErrors=false', { handleToolErrors }); } console.log( `handleToolErrors=${handleToolErrors}: on_tool_end=${onToolEnd}, on_tool_error=${onToolError}` ); } } if (require.main === module) { main().catch(console.error); } ``` ### Error Message and Stack Trace (if applicable) _No response_ ### Description * I am using langgraph stream events to sync event with the client. * I expected to get notified that a tool fails (either with on_tool_end or on_tool_error) * No event is emitted, no matter the config of ToolNode or the Tool ### System Info └─┬ @langchain/langgraph-cli 1.1.2 └─┬ @langchain/langgraph-api 1.1.2 └─┬ @hono/zod-validator 0.2.2 └── ✕ unmet peer zod@^3.19.1: found 4.2.1 in @langchain/langgraph-api Node version: v22.15.0 Operating system: darwin arm64 Package manager: pnpm Package manager version: N/A
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: langchain-ai/langgraphjs#386