[PR #173] [MERGED] fix(deepagents): resolve dangling tool_call_id when rejecting parallel tool calls #178

Closed
opened 2026-02-16 06:17:22 -05:00 by yindo · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/langchain-ai/deepagentsjs/pull/173
Author: @christian-bromann
Created: 2/3/2026
Status: Merged
Merged: 2/3/2026
Merged by: @christian-bromann

Base: mainHead: cb/dangling-tool-calls-hitl-take2


📝 Commits (3)

  • b9897bf fix(deepagents): resolve dangling tool_call_id when rejecting parallel tool calls
  • e650385 type improvements
  • c9180c7 format

📊 Changes

9 files changed (+394 additions, -170 deletions)

View changed files

📝 libs/deepagents/src/agent.int.test.ts (+19 -43)
📝 libs/deepagents/src/middleware/fs.int.test.ts (+11 -15)
📝 libs/deepagents/src/middleware/hitl.int.test.ts (+112 -42)
📝 libs/deepagents/src/middleware/index.ts (+4 -1)
📝 libs/deepagents/src/middleware/patch_tool_calls.test.ts (+143 -2)
📝 libs/deepagents/src/middleware/patch_tool_calls.ts (+87 -35)
📝 libs/deepagents/src/middleware/subagents-hitl.int.test.ts (+11 -23)
📝 libs/deepagents/src/middleware/subagents.int.test.ts (+6 -6)
📝 libs/deepagents/src/middleware/summarization.test.ts (+1 -3)

📄 Description

Fixes #150 - Rejecting an interrupted tool call no longer leaves dangling tool_call_id when two tools are called in parallel.

Problem

When a user request caused the agent to call two tools in parallel (one interrupted by HITL, one not), rejecting the interrupted tool would leave a dangling tool_call_id. The provider would then throw a 400 error:

400 An assistant message with 'tool_calls' must be followed by tool messages responding to each 'tool_call_id'. The following tool_call_ids did not have response messages: call_...

Root Cause

The createPatchToolCallsMiddleware only ran in beforeAgent to detect and patch dangling tool calls. However, when HITL rejection occurs during graph resume, state updates from beforeAgent may not be applied in time before the model is called.

Solution

Enhanced createPatchToolCallsMiddleware to patch dangling tool calls in two places:

  1. beforeAgent hook (existing): Patches state at the start of the agent loop
  2. wrapModelCall hook (new): Patches the request right before model invocation as a safety net

The wrapModelCall hook ensures that even when HITL rejection occurs during graph resume, the messages passed to the model will always have corresponding ToolMessage responses for all tool_call_ids.


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/langchain-ai/deepagentsjs/pull/173 **Author:** [@christian-bromann](https://github.com/christian-bromann) **Created:** 2/3/2026 **Status:** ✅ Merged **Merged:** 2/3/2026 **Merged by:** [@christian-bromann](https://github.com/christian-bromann) **Base:** `main` ← **Head:** `cb/dangling-tool-calls-hitl-take2` --- ### 📝 Commits (3) - [`b9897bf`](https://github.com/langchain-ai/deepagentsjs/commit/b9897bfa5bf1019a2fb3936946430fb1d865b046) fix(deepagents): resolve dangling tool_call_id when rejecting parallel tool calls - [`e650385`](https://github.com/langchain-ai/deepagentsjs/commit/e650385cb5fb8249c99ad95e3802f1b28ba1a2aa) type improvements - [`c9180c7`](https://github.com/langchain-ai/deepagentsjs/commit/c9180c7addb190a8a54ff91e8cdd5f520da0094e) format ### 📊 Changes **9 files changed** (+394 additions, -170 deletions) <details> <summary>View changed files</summary> 📝 `libs/deepagents/src/agent.int.test.ts` (+19 -43) 📝 `libs/deepagents/src/middleware/fs.int.test.ts` (+11 -15) 📝 `libs/deepagents/src/middleware/hitl.int.test.ts` (+112 -42) 📝 `libs/deepagents/src/middleware/index.ts` (+4 -1) 📝 `libs/deepagents/src/middleware/patch_tool_calls.test.ts` (+143 -2) 📝 `libs/deepagents/src/middleware/patch_tool_calls.ts` (+87 -35) 📝 `libs/deepagents/src/middleware/subagents-hitl.int.test.ts` (+11 -23) 📝 `libs/deepagents/src/middleware/subagents.int.test.ts` (+6 -6) 📝 `libs/deepagents/src/middleware/summarization.test.ts` (+1 -3) </details> ### 📄 Description Fixes #150 - Rejecting an interrupted tool call no longer leaves dangling `tool_call_id` when two tools are called in parallel. ## Problem When a user request caused the agent to call two tools in parallel (one interrupted by HITL, one not), rejecting the interrupted tool would leave a dangling `tool_call_id`. The provider would then throw a 400 error: ``` 400 An assistant message with 'tool_calls' must be followed by tool messages responding to each 'tool_call_id'. The following tool_call_ids did not have response messages: call_... ``` ## Root Cause The `createPatchToolCallsMiddleware` only ran in `beforeAgent` to detect and patch dangling tool calls. However, when HITL rejection occurs during graph resume, state updates from `beforeAgent` may not be applied in time before the model is called. ## Solution Enhanced `createPatchToolCallsMiddleware` to patch dangling tool calls in **two places**: 1. **`beforeAgent` hook** (existing): Patches state at the start of the agent loop 2. **`wrapModelCall` hook** (new): Patches the request right before model invocation as a safety net The `wrapModelCall` hook ensures that even when HITL rejection occurs during graph resume, the messages passed to the model will always have corresponding `ToolMessage` responses for all `tool_call_id`s. --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
yindo added the pull-request label 2026-02-16 06:17:22 -05:00
yindo closed this issue 2026-02-16 06:17:22 -05:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: langchain-ai/deepagentsjs#178