fix: [AI-678] add stub tool definitions for historical tool_use blocks#703
fix: [AI-678] add stub tool definitions for historical tool_use blocks#703anandgupta42 merged 3 commits intomainfrom
Conversation
The Anthropic API requires every `tool_use` block in message history to have a matching tool definition. When agents switch (Plan→Builder), MCP tools disconnect, or tools are filtered by permissions, the history may reference tools absent from the current set — causing a 400 error: "Requests with 'tool_use' and 'tool_result' blocks must include tool definition." Replace the LiteLLM-only `_noop` workaround with a general fix: - Extract all tool names from `tool-call` blocks in message history - Add stub definitions for any names missing from the active tools set - Stubs return "tool no longer available" if the model attempts to call them Closes #678 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Claude Code Review
This repository is configured for manual code reviews. Comment @claude review to trigger a review and subscribe this PR to future pushes, or @claude review once for a one-time review.
Tip: disable this comment in your organization's Code Review settings.
📝 WalkthroughWalkthroughReplaced provider-specific noop tool injection with a generic stub-tool mechanism that extracts referenced tool names from message history and injects stub definitions for missing tools. Replaced Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
… dead code Multi-model review (GPT 5.4, Gemini 3.1 Pro) identified three issues: - `toolNamesFromMessages()` now scans both `tool-call` AND `tool-result` blocks, guarding against orphaned tool-results in compacted histories - Use `Object.hasOwn()` instead of direct property check to avoid prototype pollution edge case (`toString`, `constructor`) - Remove dead `hasToolCalls()` function and its tests — sole call site was the LiteLLM workaround deleted in the previous commit Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/opencode/src/session/llm.ts`:
- Around line 287-295: The toolNamesFromMessages function currently only records
parts with type "tool-call"; update it so it also captures tool-result variants
(e.g., "tool-result" and "tool_result") by checking part.type for those values
and adding part.toolName (guarding that it exists) to the names set; modify the
loop in toolNamesFromMessages to include these additional type checks so
historical tool-result entries produce stubs just like tool-call entries.
In `@packages/opencode/test/session/llm.test.ts`:
- Around line 57-65: The test is asserting the old broken behavior by expecting
an empty set from LLM.toolNamesFromMessages for a message containing a
tool-result; update the test to expect the tool name to be returned instead
(i.e., the set should contain "bash") so LLM.toolNamesFromMessages returns new
Set(["bash"]) for the provided messages; locate the test in
packages/opencode/test/session/llm.test.ts and change the expect(...) assertion
accordingly to reflect the corrected behavior needed for the Anthropic
validation path.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: f685342b-dede-4153-8a61-b16cd17739a2
📒 Files selected for processing (2)
packages/opencode/src/session/llm.tspackages/opencode/test/session/llm.test.ts
There was a problem hiding this comment.
1 issue found across 2 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="packages/opencode/src/session/llm.ts">
<violation number="1" location="packages/opencode/src/session/llm.ts:292">
P1: `tool-result` blocks also carry `toolName` and trigger the same Anthropic validation error when no matching tool definition exists. If history trimming leaves an orphaned `tool-result` without its paired `tool-call`, this scan will miss it and the API will still return a 400. Include `part.type === "tool-result"` in the condition.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review, or fix all with cubic.
packages/opencode/src/session/llm.ts
Outdated
| for (const msg of messages) { | ||
| if (!Array.isArray(msg.content)) continue | ||
| for (const part of msg.content) { | ||
| if (part.type === "tool-call") names.add(part.toolName) |
There was a problem hiding this comment.
P1: tool-result blocks also carry toolName and trigger the same Anthropic validation error when no matching tool definition exists. If history trimming leaves an orphaned tool-result without its paired tool-call, this scan will miss it and the API will still return a 400. Include part.type === "tool-result" in the condition.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/opencode/src/session/llm.ts, line 292:
<comment>`tool-result` blocks also carry `toolName` and trigger the same Anthropic validation error when no matching tool definition exists. If history trimming leaves an orphaned `tool-result` without its paired `tool-call`, this scan will miss it and the API will still return a 400. Include `part.type === "tool-result"` in the condition.</comment>
<file context>
@@ -276,4 +278,21 @@ export namespace LLM {
+ for (const msg of messages) {
+ if (!Array.isArray(msg.content)) continue
+ for (const part of msg.content) {
+ if (part.type === "tool-call") names.add(part.toolName)
+ }
+ }
</file context>
| if (part.type === "tool-call") names.add(part.toolName) | |
| if (part.type === "tool-call" || part.type === "tool-result") { | |
| names.add(part.toolName) | |
| } |
What does this PR do?
Fixes the Anthropic API error "Requests with 'tool_use' and 'tool_result' blocks must include tool definition" that occurs when conversation history references tools no longer in the active set (e.g., after switching agents from Plan→Builder, MCP tools disconnecting, or tools filtered by permissions).
Replaces the previous LiteLLM-only
_noopworkaround with a general fix that:tool-callblocks in message historyType of change
Issue for this PR
Closes #678
How did you verify your code works?
toolNamesFromMessages()covering: empty messages, no tool calls, extraction, dedup, tool-call vs tool-result distinctionllm.test.tspass (5 new + 10 existing)bun run ci— unit tests + marker guard)Checklist
Summary by cubic
Prevents Anthropic validation errors by adding stub tool definitions for historical
tool_useblocks when tools are no longer active. Replaces the LiteLLM-only_noopworkaround with a provider-agnostic fix for AI-678.tool-callandtool-result, then adds stubs for any missing in the active set.Object.hasOwn()for safe presence checks; removes_noopand deadhasToolCalls(); adds tests fortoolNamesFromMessages(extraction, dedup, results).Written for commit f7fdcc8. Summary will update on new commits.
Summary by CodeRabbit
Bug Fixes
Tests