You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
tool_choice: 'required' incompatible with thinking-enabled models when using structured output #15226
When using format: { type: "json_schema" } with the prompt_async API, OpenCode unconditionally sets toolChoice: "required" for the internal StructuredOutput tool. For reasoning models like Kimi K2.5
that have thinking automatically enabled, the downstream provider rejects this with:
tool_choice 'required' is incompatible with thinking enabled
This affects OpenCode Go (opencode-go). The same model + structured output works via OpenCode Zen (opencode), suggesting Zen reconciles the conflict server-side.
OpenCode Version
1.2.14
Plugins
No response
OpenCode version
No response
Steps to reproduce
Start an OpenCode server with an opencode-go API key
Create a session
Send prompt_async with json_schema format targeting a reasoning model:
Both tool_choice: "required" and thinking params are sent together to the downstream provider, which rejects the combination.
Affected Models
Any model with reasoning: true accessed via providers that enforce thinking + tool_choice incompatibility. Confirmed: kimi-k2.5 (Moonshot AI) via opencode-go.
Screenshot and/or share link
No response
Operating System
No response
Terminal
No response
Reacted by ylxdzsw, Yuval Yacoby, Nick Wang, Adrian Ani , Michael Agra and Harry Touloupas
I had the same issue using the version 1.2.27 of opencode with Qwen3.5-9B as model.
It seems that I found a workaround specifying the following prompt for an agent in opencode.json:
"prompt": "If a tool or a command returns null, none, or empty values, do not assume it is a failure. Instead, analyze the situation and make a decision on how to proceed based on the context. Consider alternative approaches, gather more information, or adjust your strategy as needed to achieve the best outcome."
in opencode.json I also have the following configuration:
We're running into this issue as well when setting format as well as an anthropic variant with thinking: true for the model config. Is there a solution that allows structured output and thinking for anthropic models?
@kitlangton
Running into the same issue with deepseek-v4-flash-free when format option is given while calling from opencode sdk.
{message: "Error from provider (DeepSeek): Thinking mode does not support this tool_choice",statusCode: 400,isRetryable: false,responseHeaders: {"cf-placement": "remote-ORD","cf-ray": "a0237c8998767f72-IAD",connection: "keep-alive","content-length": "178","content-type": "application/octet-stream",date: "Wed, 27 May 2026 08:00:33 GMT",server: "cloudflare",},responseBody: "{\"error\":{\"message\":\"Error from provider (DeepSeek): Thinking mode does not support this tool_choice\",\"type\":\"invalid_request_error\",\"param\":null,\"code\":\"invalid_request_error\"}}",metadata: {url: "https://opencode.ai/zen/v1/chat/completions",},}
I am calling it like this
import{createOpencodeClient}from"@opencode-ai/sdk"import{z}from"zod"constoc=createOpencodeClient({baseUrl: `http://localhost:${process.env.OPENCODE_SERVER_PORT}`,responseStyle: "data",})constmain=async()=>{constresponse=awaitoc.session.prompt({query: {directory: folderLocation},path: {id: sessionId},body: {model: {providerID: "opencode",modelID: "deepseek-v4-flash-free"},agent: "build",tools: {question: false},parts: [{type: "text",text: ["my prompt here"]}],// @ts-expect-error - format is not supported in the opencode SDK types, but it is supported in the APIformat: {type: "json_schema",schema: z.toJSONSchema(MySchema)}},})}main()
This even messes up the OpenCode Desktop app when that particular session is clicked. You'll have to delete the session or affected messages from sqlite for the app to function again.
The regression covers the structured-output + thinking path where the final provider payload used to force tool_choice: "required"; the fix relaxes that to auto for thinking/reasoning provider surfaces before the runtime sends the request. That should cover the DeepSeek/OpenCode SDK case reported above as well, since it goes through the same structured-output request preparation path.
Refiled as #46735, per the auto-close note asking for a new issue.
The new report adds a live reproduction with controls: claude-fable-5-1 returns 400 on {"type":"any"} and {"type":"tool"} while accepting auto/none, and claude-fable-5 accepts all four — so it is model-gated, and the failure is attributable to tool_choice rather than to request shape. Also an end-to-end capture showing opencode's required arriving on the wire as {"type":"any"}.
The cross-provider reports in this thread (@bralca on Kimi K2.5, @lucad87 on Qwen3.5-9B, @alex-wesolowski-cb on Anthropic with thinking, @dineshgadge on DeepSeek v4 flash) are cited there as evidence this is one class rather than four separate provider quirks. @serejaris, your #29565 is credited as the narrower fix; the new issue argues for the provider's native structured output as the default since it avoids the forced choice entirely, with your compatibleToolChoice shape as the alternative.
@rscambler — you asked here whether #29565 could be reopened and got no answer. It cannot be reopened by a non-maintainer, so #46735 is the path.
Activity
github-actions commentedon Feb 26, 2026
This issue might be a duplicate of or related to existing issues. Please check:
lucad87 commentedon Mar 18, 2026
I had the same issue using the version 1.2.27 of opencode with Qwen3.5-9B as model.
It seems that I found a workaround specifying the following prompt for an agent in opencode.json:
in opencode.json I also have the following configuration:
alex-wesolowski-cb commentedon Mar 24, 2026
We're running into this issue as well when setting
formatas well as an anthropic variant withthinking: truefor the model config. Is there a solution that allows structured output and thinking for anthropic models?nickwang0808 commentedon May 4, 2026
ran into the same issue as well
dineshgadge commentedon May 27, 2026
@kitlangton
Running into the same issue with deepseek-v4-flash-free when
formatoption is given while calling from opencode sdk.I am calling it like this
This even messes up the OpenCode Desktop app when that particular session is clicked. You'll have to delete the session or affected messages from sqlite for the app to function again.
serejaris commentedon May 27, 2026
I opened #29565 for this request-shape conflict.
The regression covers the structured-output + thinking path where the final provider payload used to force
tool_choice: "required"; the fix relaxes that toautofor thinking/reasoning provider surfaces before the runtime sends the request. That should cover the DeepSeek/OpenCode SDK case reported above as well, since it goes through the same structured-output request preparation path.github-actions commentedon Jul 27, 2026
To stay organized issues are automatically closed after 60 days of no activity. If the issue is still relevant please open a new one.
iceteaSA commentedon Sep 2, 2026
Refiled as #46735, per the auto-close note asking for a new issue.
The new report adds a live reproduction with controls:
claude-fable-5-1returns 400 on{"type":"any"}and{"type":"tool"}while acceptingauto/none, andclaude-fable-5accepts all four — so it is model-gated, and the failure is attributable totool_choicerather than to request shape. Also an end-to-end capture showing opencode'srequiredarriving on the wire as{"type":"any"}.The cross-provider reports in this thread (@bralca on Kimi K2.5, @lucad87 on Qwen3.5-9B, @alex-wesolowski-cb on Anthropic with thinking, @dineshgadge on DeepSeek v4 flash) are cited there as evidence this is one class rather than four separate provider quirks. @serejaris, your #29565 is credited as the narrower fix; the new issue argues for the provider's native structured output as the default since it avoids the forced choice entirely, with your
compatibleToolChoiceshape as the alternative.@rscambler — you asked here whether #29565 could be reopened and got no answer. It cannot be reopened by a non-maintainer, so #46735 is the path.
fix(opencode): native output_format structured output (works with thi…
fix(opencode): native output_format structured output (works with thi…
fix(opencode): native output_format structured output (works with thi…