A developer building a code assistant notices that tool_use response blocks are being returned by Claude but the tool results are sometimes ignored by subsequent Claude turns — Claude proceeds as if the tool was never called. Debugging shows the conversation history is being correctly appended. The issue occurs specifically when tool_result blocks are returned with is_error: true. What is the most likely root cause and fix?
Select an answer to reveal the explanation.
Short Explanation
Here's the deal — if the tool result's tool_use_id doesn't match the tool_use_id from Claude's tool_use block, Claude can't link the result to the call, so it proceeds as if the tool never ran. Fix it by copying the exact tool_use_id from the tool_use block into the matching tool_result block, including in error paths.
Full Explanation
The tool_use_id mismatch is the classic cause of tool results being treated as ignored. Every tool_use block Claude emits contains a unique tool_use_id, and the corresponding tool_result block in the next user message must reference the exact same tool_use_id to establish the linkage. If the developer's code generates a new identifier, copies it incorrectly, or has a serialization error, the identifiers will not match; Claude cannot associate the result with the original tool call and proceeds without tool grounding. This can surface specifically with is_error: true results when the error-handling code path has a bug in identifier propagation that the success path does not. The explanation that Claude's API filters out tool_result blocks marked is_error: true, and that the fix is to set is_error to false and put failure text in the content JSON, is incorrect; Claude processes is_error: true results, and the error flag is informational, not a filter trigger. The explanation that the is_error flag is being attached to the user message wrapper instead of the tool_result block, causing Claude to treat the payload as ordinary user text and ignore the tool_use_id, points to a structural or schema mistake, but it is not the most likely root cause here. Tool_result blocks must be properly nested inside a user message content array, and is_error belongs on the tool_result block, yet the decisive linkage is the matching tool_use_id; a misplaced flag alone does not explain the error-path-specific silent drop as directly as an identifier mismatch. The explanation that error tool results should be sent as assistant messages with is_error: true, because tool_result blocks are only valid for successful calls, is also wrong: tool_result blocks are valid for failed calls, and the result should be returned as a tool_result block in the next user message, not as an assistant message. For completeness, error tool results do not require a non-empty content string; an empty string is allowed, and the API does not silently discard empty-content error results.