[Returning Issue] Claude Sonnet Thinking Mode incompatible with tool use - "Expected thinking or redacted_thinking, but found text" error
Claude Sonnet Thinking Mode incompatible with tool use (regression in v1.2.5)
Hello @mattapperson,
I'm reopening this issue as it has resurfaced in the latest version.
Summary
Issue #177 was successfully resolved in version 1.2.1, which fixed the Claude Sonnet compatibility with Reasoning mode and Tool use. However, maybe the changes introduced in version 1.2.5 have caused a regression and now Claude Sonnet gives the original error: `"Expected thinking or redacted_thinking, but found text" when Reasoning is used with Tools.
Issue Details
The commit "Add Google Gemini reasoning_details support and fix multi-turn preservation" successfully resolved the Gemini 3 Pro Preview Reasoning integration, and maybe reintroduced the Claude Sonnet issue.
References
-
Original fix for Claude Sonnet (v1.2.1):
https://github.com/OpenRouterTeam/ai-sdk-provider/pull/207/files -
Commit that maybe caused regression (v1.2.5):
https://github.com/OpenRouterTeam/ai-sdk-provider/commit/c948aba56cb887533d329f6715cebfe3af238fa8
It appears we now have a situation where Gemini 3 Pro Preview Reasoning with Tools works correctly, but Claude Sonnet has regressed to the original error: "Expected thinking or redacted_thinking, but found text".
Thank you for your attention to this matter.
Hmmm we do have a test that is passing here so I am thinking there is a perhaps a blind spot in terms of the AI SDK... can you provide an example of this failing?
@mattapperson Hello, I compared the differences in the second round of tool calls between two different versions of the OpenRouter AI SDK. The correct response version does not include reasoning_details.
Here is the correct version:
Here is the incorrect version:
Since I'm not sure how OpenRouter handles the conversion of reasoning_details, it might require an official developer to fix this bug.
I can reproduce it for Haiku 4.5 with pdf file attached:
"{\"error\":{\"message\":\"Provider returned error\",\"code\":400,\"metadata\":{\"raw\":\"
{\\\"message\\\":\\\"messages.1.content.0.type: Expected `thinking` or `redacted_thinking`,
but found `tool_use`. When `thinking` is enabled, a final `assistant`
message must start with a thinking block
(preceeding the lastmost set of `tool_use` and `tool_result` blocks).
We recommend you include thinking blocks from previous turns.
To avoid this requirement, disable `thinking`. Please consult our documentation at https://docs.claude.com/en/docs/build-with-claude/extended-thinking\\\"}\",
\"provider_name\":\"Amazon Bedrock\"}},\"user_id\":\"***\"}","isRetryable":false,"data":{"error":{"code":400,"message":"Provider returned error","type":null,"param":null,"metadata":{"raw":"{\"message\":\"messages.1.content.0.type: Expected `thinking` or `redacted_thinking`, but found `tool_use`. When `thinking` is enabled, a final `assistant` message must start with a thinking block (preceeding the lastmost set of `tool_use` and `tool_result` blocks). We recommend you include thinking blocks from previous turns. To avoid this requirement, disable `thinking`. Please consult our documentation at https://docs.claude.com/en/docs/build-with-claude/extended-thinking\"}","provider_name":"Amazon Bedrock"}},"user_id":"***"}}] [message=Provider returned error]
@mattapperson Hello! I can reproduce the issue consistently.
Environment
-
@openrouter/ai-sdk-provider:1.2.5 -
ai:^5.0.101 - Model:
anthropic/claude-sonnet-4.5
Test Code
import { createOpenRouter } from '@openrouter/ai-sdk-provider';
import { streamText } from 'ai';
const openrouter = createOpenRouter({
apiKey: 'your-api-key',
extraBody: {
reasoning: {
exclude: false,
max_tokens: 3500,
},
},
});
const model = openrouter('anthropic/claude-sonnet-4.5');
const stream = streamText({
system: 'You are a helpful assistant. You must call the test_tool once on every request.',
model,
messages: [
{
role: 'user',
content: 'Hello, how are you today?'
}
],
maxOutputTokens: 64000,
stopWhen: stepCountIs(5),
tools: {
test_tool: tool({
description: 'A test tool that you MUST call once on every single request before responding. Always call this tool first.',
inputSchema: z.object({
message: z.string().describe('Any message to pass to the tool'),
}),
execute: async ({ message }) => {
return 'Tool was called successfully! Don\'t call anymore this tool and provide an answer to the user.';
},
}),
},
onError: (e: any) => {
console.error(`[STREAM_ERROR]: ${JSON.stringify(e) || 'Unknown'}...`);
},
onStepFinish: async (event) => {
console.error(`[STEP_FINISH]: ${JSON.stringify(event) || 'Unknown'}...`);
},
});
Error (v1.2.5)
First tool call succeeds, but the second API call fails with:
{
"error": {
"type": "invalid_request_error",
"message": "messages.1.content.0.type: Expected `thinking` or `redacted_thinking`, but found `tool_use`. When `thinking` is enabled, a final `assistant` message must start with a thinking block."
}
}
Root Cause
v1.2.5 - Incorrectly includes reasoning_details in request body:
{
"role": "assistant",
"content": "",
"tool_calls": [...],
"reasoning": "The user is greeting me...",
"reasoning_details": [{
"type": "reasoning.text",
"text": "The user is greeting me...",
"format": "anthropic-claude-v1"
}]
}
v1.2.1 - Does not include reasoning_details in request body:
{
"role": "assistant",
"content": "",
"tool_calls": [...],
"reasoning": "The user is greeting me..."
}
When reasoning_details with type "reasoning.text" is sent to Anthropic's API, it expects the content to be an array starting with a thinking block, not with tool_use. v1.2.1 correctly excluded reasoning_details from the request body, while v1.2.5 incorrectly includes it.
This appears to be a regression from commit c948aba that added Gemini reasoning_details support.
Reproduction
- Install
@openrouter/[email protected] - Use Claude Sonnet with reasoning enabled + any tool
- Error occurs on second API call after tool execution
- Downgrade to v1.2.1 - works fine
I am very sorry for this issue and please note that we are both working on a fix for the existing codebase as well as a complete re-write of the provider to make it more reliable and based on the new OpenRouter Typescript SDK. We will get a fix out for this as soon as possible
i've added a pull request for this issue. doing investigation with opus 4.5 i found further issue in terms of the scoping of the accumulator - which could have resulted in reasoning be collected over the entire conversation and then appended to every assistant message.
also worth noting that the e2e tests were failing for me prior to applying the fixes in the pr. might be a ci issue.
I'd appreciate if this could get looked at soon by the team.
the issue has caused a regression in a feature but at the same time we rolled out gemini use for another feature, so we are stuck in a see saw situation of only being able to support one or the other. 😅
@seannetlife found a fix for this here: https://github.com/OpenRouterTeam/ai-sdk-provider/pull/258
I also included your test code as an e2e test -- that was super helpful!
legend. thank you @louisgv ! upgrading now
unfortunately the issue has come back in a slightly different form, but sounds related:
{
"type": "error",
"error": {
"type": "invalid_request_error",
"message": "messages.3.content.1: `thinking` or `redacted_thinking` blocks in the latest assistant message cannot be modified. These blocks must remain as they were in the original response."
},
"request_id": "req_011CVhM4qgBMCCJ2kAA5MX6B"
}