[GH-ISSUE #5376] [BUG]: Claude provider crashes with 400 error when tool call limit is reached mid-turn (missing tool_result injection in aibitat/index.js) #5055

Closed
opened 2026-06-05 14:51:45 -04:00 by yindo · 1 comment
Owner

Originally created by @ncimino on GitHub (Apr 7, 2026).
Original GitHub issue: https://github.com/Mintplex-Labs/anything-llm/issues/5376

Originally assigned to: @timothycarambat on GitHub.

Describe the bug

When using AnythingLLM with the Anthropic (Claude) provider and an agent that uses tools (including MCP tools), the agent throws a 400 invalid_request_error when the internal tool call limit is reached after Claude has already emitted a tool_use block.

Claude's API strictly requires that every tool_use block in an assistant message must be followed by a corresponding tool_result block in the very next user message. When aibitat/index.js hits its tool call limit and jumps directly to requesting a final response, it leaves one or more tool_use blocks unmatched — which Claude rejects outright.


Steps to Reproduce

  1. Configure an AnythingLLM agent with the Anthropic/Claude provider
  2. Attach multiple tools (e.g., via MCP or built-in agent tools)
  3. Send a prompt that causes the agent to invoke tools in sequence until the internal tool call limit is hit
  4. Observe a 400 invalid_request_error from the Anthropic API on the final completion request

Expected Behavior

When the tool call limit is reached and a tool_use block has already been emitted by Claude, AnythingLLM should inject a synthetic tool_result message for each unresolved tool_use ID before making the final response request. For example:

{
  "role": "user",
  "content": [
    {
      "type": "tool_result",
      "tool_use_id": "<unresolved_tool_use_id>",
      "content": "Tool call limit reached, this tool was not executed."
    }
  ]
}

Actual Behavior

The agent throws a 400 error from the Anthropic API:

tool_use ids were found without tool_result blocks immediately after

The conversation fails entirely with no response returned to the user.


Location of Bug

server/utils/agents/aibitat/index.js — approximately lines 814–854

This is the section that handles the tool call limit check and transitions to the final response. It does not currently account for any pending/unresolved tool_use blocks when the Claude provider is in use.


Suggested Fix

Before making the final completion request after hitting the tool call limit, check whether the last assistant message contains any tool_use blocks. If so, inject a tool_result user message for each unresolved tool_use_id:

// After hitting tool call limit, before final LLM request:
const lastMessage = messages[messages.length - 1];
if (lastMessage?.role === "assistant" && Array.isArray(lastMessage.content)) {
  const unresolvedToolUses = lastMessage.content.filter(b => b.type === "tool_use");
  if (unresolvedToolUses.length > 0) {
    messages.push({
      role: "user",
      content: unresolvedToolUses.map(block => ({
        type: "tool_result",
        tool_use_id: block.id,
        content: "Tool call limit reached, this tool was not executed.",
      })),
    });
  }
}

Environment

  • Provider: Anthropic / Claude (claude-3-5-sonnet, claude-3-7-sonnet, etc.)
  • Feature: AI Agents with tool use / MCP tools
  • Component: server/utils/agents/aibitat/index.js

Additional Context

This is not a bug in Claude or the MCP protocol. Claude is behaving correctly per the Anthropic API spec. The fix belongs entirely in AnythingLLM's aibitat layer. Other providers (OpenAI, etc.) are more lenient with unmatched tool calls, which is why this only surfaces with Anthropic.

Originally created by @ncimino on GitHub (Apr 7, 2026). Original GitHub issue: https://github.com/Mintplex-Labs/anything-llm/issues/5376 Originally assigned to: @timothycarambat on GitHub. **Describe the bug** When using AnythingLLM with the Anthropic (Claude) provider and an agent that uses tools (including MCP tools), the agent throws a `400 invalid_request_error` when the internal tool call limit is reached **after** Claude has already emitted a `tool_use` block. Claude's API strictly requires that every `tool_use` block in an assistant message must be followed by a corresponding `tool_result` block in the very next user message. When `aibitat/index.js` hits its tool call limit and jumps directly to requesting a final response, it leaves one or more `tool_use` blocks unmatched — which Claude rejects outright. *** **Steps to Reproduce** 1. Configure an AnythingLLM agent with the Anthropic/Claude provider 2. Attach multiple tools (e.g., via MCP or built-in agent tools) 3. Send a prompt that causes the agent to invoke tools in sequence until the internal tool call limit is hit 4. Observe a `400 invalid_request_error` from the Anthropic API on the final completion request *** **Expected Behavior** When the tool call limit is reached and a `tool_use` block has already been emitted by Claude, AnythingLLM should inject a synthetic `tool_result` message for each unresolved `tool_use` ID before making the final response request. For example: ```json { "role": "user", "content": [ { "type": "tool_result", "tool_use_id": "<unresolved_tool_use_id>", "content": "Tool call limit reached, this tool was not executed." } ] } ``` *** **Actual Behavior** The agent throws a `400` error from the Anthropic API: ``` tool_use ids were found without tool_result blocks immediately after ``` The conversation fails entirely with no response returned to the user. *** **Location of Bug** `server/utils/agents/aibitat/index.js` — approximately lines **814–854** This is the section that handles the tool call limit check and transitions to the final response. It does not currently account for any pending/unresolved `tool_use` blocks when the Claude provider is in use. *** **Suggested Fix** Before making the final completion request after hitting the tool call limit, check whether the last assistant message contains any `tool_use` blocks. If so, inject a `tool_result` user message for each unresolved `tool_use_id`: ```js // After hitting tool call limit, before final LLM request: const lastMessage = messages[messages.length - 1]; if (lastMessage?.role === "assistant" && Array.isArray(lastMessage.content)) { const unresolvedToolUses = lastMessage.content.filter(b => b.type === "tool_use"); if (unresolvedToolUses.length > 0) { messages.push({ role: "user", content: unresolvedToolUses.map(block => ({ type: "tool_result", tool_use_id: block.id, content: "Tool call limit reached, this tool was not executed.", })), }); } } ``` *** **Environment** - **Provider**: Anthropic / Claude (claude-3-5-sonnet, claude-3-7-sonnet, etc.) - **Feature**: AI Agents with tool use / MCP tools - **Component**: `server/utils/agents/aibitat/index.js` *** **Additional Context** This is **not** a bug in Claude or the MCP protocol. Claude is behaving correctly per the Anthropic API spec. The fix belongs entirely in AnythingLLM's aibitat layer. Other providers (OpenAI, etc.) are more lenient with unmatched tool calls, which is why this only surfaces with Anthropic.
yindo closed this issue 2026-06-05 14:51:45 -04:00
Author
Owner

@timothycarambat commented on GitHub (Apr 7, 2026):

thanks for reporting, just improved that flow now

<!-- gh-comment-id:4201463479 --> @timothycarambat commented on GitHub (Apr 7, 2026): thanks for reporting, just improved that flow now
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: Mintplex-Labs/anything-llm#5055