Compare commits

..

1 Commits

Author SHA1 Message Date
Claude
20c5837683 docs: Add bug report for duplicate tool_result blocks issue
Documents API error where multiple tool_result blocks are sent with
the same tool_use_id when a tool fails. The "Sibling tool call errored"
message is incorrectly being sent for the same tool that failed,
rather than only for other sibling tools in a parallel batch.

Includes:
- Root cause analysis
- Example JSON from reported incidents
- Suggested fix locations and pseudocode
- Related request IDs for debugging
2025-12-08 01:19:25 +00:00
4 changed files with 124 additions and 115 deletions

View File

@@ -0,0 +1,123 @@
# Bug Report: Duplicate tool_result blocks with same tool_use_id
## Summary
When a tool call fails (particularly observed with the Tmux tool), two `tool_result` blocks are incorrectly sent to the API with the same `tool_use_id`, causing an API error:
```
API Error: 400
{"type":"error","error":{"type":"invalid_request_error","message":"messages.X.content.1: each tool_use must have a single result. Found multiple `tool_result` blocks with id: toolu_XXX"}}
```
## Root Cause
The parallel tool execution error handling code sends a "Sibling tool call errored" message even for the tool that actually failed, when it should only send this message for OTHER sibling tools in the parallel batch.
## Reproduction
1. Execute a tool call that will fail (e.g., Tmux select-pane on a non-existent window)
2. Observe two tool_result messages being generated:
- First: The actual error result ("Window not found...")
- Second: A "<tool_use_error>Sibling tool call errored</tool_use_error>" message with the SAME tool_use_id
## Example JSON (from reported incident)
```json
// Assistant requests tool use
{
"type": "tool_use",
"id": "toolu_01K2K2KFvUrwA9PaHGp652zW",
"name": "Tmux",
"input": {"args": ["select-pane", "-t", "test-perms3:0.0"]}
}
// First tool_result (correct - actual error)
{
"type": "user",
"message": {
"role": "user",
"content": [{
"tool_use_id": "toolu_01K2K2KFvUrwA9PaHGp652zW",
"type": "tool_result",
"content": "Window not found. The specified window may not exist.",
"is_error": true
}]
}
}
// Second tool_result (BUG - same tool_use_id!)
{
"type": "user",
"message": {
"role": "user",
"content": [{
"type": "tool_result",
"content": "<tool_use_error>Sibling tool call errored</tool_use_error>",
"is_error": true,
"tool_use_id": "toolu_01K2K2KFvUrwA9PaHGp652zW"
}]
}
}
```
## Expected Behavior
- When a tool fails, only ONE tool_result should be sent for that tool_use_id
- The "Sibling tool call errored" message should ONLY be sent for OTHER tool_use_ids in a parallel batch, not for the tool that actually errored
## Suggested Fix Location
The fix should be in the code that handles parallel tool execution errors:
```typescript
// Pseudocode for the fix
function handleParallelToolResults(toolUseIds: string[], failedToolId: string, error: Error) {
const results = [];
for (const id of toolUseIds) {
if (id === failedToolId) {
// Send actual error for the failed tool
results.push({
tool_use_id: id,
type: "tool_result",
content: error.message,
is_error: true
});
} else {
// Only send sibling error for OTHER tools
results.push({
tool_use_id: id,
type: "tool_result",
content: "<tool_use_error>Sibling tool call errored</tool_use_error>",
is_error: true
});
}
}
return results;
}
```
Additionally, a deduplication check before sending to the API would prevent this:
```typescript
function deduplicateToolResults(messages: Message[]): Message[] {
const seenToolUseIds = new Set<string>();
return messages.filter(msg => {
if (msg.type === "tool_result") {
if (seenToolUseIds.has(msg.tool_use_id)) {
return false; // Skip duplicate
}
seenToolUseIds.add(msg.tool_use_id);
}
return true;
});
}
```
## Related CHANGELOG Fixes
Similar issues have been fixed before:
- v1.0.84: "Fix tool_use/tool_result id mismatch error when network is unstable"
- v2.0.0: "Hooks: Reduced PostToolUse 'tool_use' ids were found without 'tool_result' blocks errors"
- v2.0.22: "Fix bug causing duplicate permission prompts with parallel tool calls"
## Reported By
Issue reported via Slack with multiple reproductions showing the Tmux tool triggering this bug.
## Request IDs (for debugging)
- req_011CVt2KXhCGmrL2YN9sSZQP
- req_011CVtFYJLPxn9oB9FEkCnRr
- req_011CVtG1PREveTLikakXGEdn

View File

@@ -1,40 +1,5 @@
# Changelog
## 2.0.65
- Added ability to switch models while writing a prompt using alt+p (linux, windows), option+p (macos).
- Added context window information to status line input
- Added `fileSuggestion` setting for custom `@` file search commands
- Added `CLAUDE_CODE_SHELL` environment variable to override automatic shell detection (useful when login shell differs from actual working shell)
- Fixed prompt not being saved to history when aborting a query with Escape
- Fixed Read tool image handling to identify format from bytes instead of file extension
## 2.0.64
- Made auto-compacting instant
- Agents and bash commands can run asynchronously and send messages to wake up the main agent
- /stats now provides users with interesting CC stats, such as favorite model, usage graph, usage streak
- Added named session support: use `/rename` to name sessions, `/resume <name>` in REPL or `claude --resume <name>` from the terminal to resume them
- Added support for .claude/rules/`. See https://code.claude.com/docs/en/memory for details.
- Added image dimension metadata when images are resized, enabling accurate coordinate mappings for large images
- Fixed auto-loading .env when using native installer
- Fixed `--system-prompt` being ignored when using `--continue` or `--resume` flags
- Improved `/resume` screen with grouped forked sessions and keyboard shortcuts for preview (P) and rename (R)
- VSCode: Added copy-to-clipboard button on code blocks and bash tool inputs
- VSCode: Fixed extension not working on Windows ARM64 by falling back to x64 binary via emulation
- Bedrock: Improve efficiency of token counting
- Unshipped AgentOutputTool and BashOutputTool, in favor of a new unified TaskOutputTool
## 2.0.62
- Added "(Recommended)" indicator for multiple-choice questions, with the recommended option moved to the top of the list
- Added `attribution` setting to customize commit and PR bylines (deprecates `includeCoAuthoredBy`)
- Fixed duplicate slash commands appearing when ~/.claude is symlinked to a project directory
- Fixed slash command selection not working when multiple commands share the same name
- Fixed an issue where skill files inside symlinked skill directories could become circular symlinks
- Fixed running versions getting removed because lock file incorrectly going stale
- Fixed IDE diff tab not closing when rejecting file changes
## 2.0.61
- Reverted VSCode support for multiple terminal clients due to responsiveness issues.

View File

@@ -42,7 +42,6 @@ assistant: "[How assistant should respond and use this agent]"
model: inherit
color: blue
tools: ["Read", "Write", "Grep"]
skills: skill-name-1, skill-name-2
---
You are [agent role description]...
@@ -160,35 +159,6 @@ tools: ["Read", "Write", "Grep", "Bash"]
- Testing: `["Read", "Bash", "Grep"]`
- Full access: Omit field or use `["*"]`
### skills (optional)
Declare skills to auto-load when this agent is invoked.
**Format:** Comma-separated list of skill names
```yaml
skills: database-migration, postgres-ops
```
**Purpose:** Ensures specific skills are always available to the agent, regardless of context matching. This is especially useful for:
- Skills that should persist across context compaction
- Specialized agents that always need certain domain knowledge
- Ensuring consistent agent behavior with required skills
**Example:**
```yaml
---
name: db-admin
description: Use this agent for database administration tasks...
model: inherit
color: cyan
tools: ["Bash", "Read", "Write"]
skills: database-migration, sql-optimization
---
```
**Best practice:** Only declare skills essential to the agent's core function. Let context-triggered skills load naturally for optional capabilities.
## System Prompt Design
The markdown body becomes the agent's system prompt. Write in second person, addressing the agent directly.
@@ -385,7 +355,6 @@ Output: [What to provide]
| model | Yes | inherit/sonnet/opus/haiku | inherit |
| color | Yes | Color name | blue |
| tools | No | Array of tool names | ["Read", "Grep"] |
| skills | No | Comma-separated names | skill-a, skill-b |
### Best Practices

View File

@@ -12,7 +12,6 @@ description: Brief description
allowed-tools: Read, Write
model: sonnet
argument-hint: [arg1] [arg2]
skills: skill-a, skill-b
---
Command prompt content here...
@@ -322,51 +321,6 @@ disable-model-invocation: true
- Document why in command comments
- Consider if command should exist if always manual
### skills
**Type:** String (comma-separated list)
**Required:** No
**Default:** None
**Purpose:** Declare skills to auto-load when this command executes. Ensures specific skills are always available regardless of context matching.
**Examples:**
```yaml
skills: database-migration, sql-optimization
```
**When to use:**
1. **Commands requiring domain knowledge:** Ensure relevant skills load
```yaml
---
description: Run database migration
skills: database-migration, postgres-ops
---
```
2. **Skills that should persist across compaction:** Force-load skills that might otherwise be lost
```yaml
---
description: Complex multi-step workflow
skills: workflow-skill, validation-skill
---
```
3. **Specialized commands:** Commands that always need specific expertise
```yaml
---
description: Generate API documentation
skills: api-documentation, openapi-spec
---
```
**Best practices:**
- Only declare skills essential to the command's function
- Use skill names exactly as defined in SKILL.md frontmatter
- Keep the list focused (2-4 skills max)
- Let context-triggered skills load naturally for optional capabilities
## Complete Examples
### Minimal Command
@@ -497,7 +451,6 @@ Before committing command:
- [ ] model is valid value if specified
- [ ] argument-hint matches positional arguments
- [ ] disable-model-invocation used appropriately
- [ ] skills references valid skill names if specified
## Best Practices Summary
@@ -507,5 +460,4 @@ Before committing command:
4. **Choose right model:** Use haiku for speed, opus for complexity
5. **Manual-only sparingly:** Only use disable-model-invocation when necessary
6. **Clear descriptions:** Make commands discoverable in `/help`
7. **Declare essential skills:** Use skills field for domain knowledge that must persist
8. **Test thoroughly:** Verify frontmatter works as expected
7. **Test thoroughly:** Verify frontmatter works as expected