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
Config tool_call: false does not disable tools #35432
tool_call: false in model config is ignored by the prompt loop. The code unconditionally resolves SessionTools and sends them in the LLM request body, with tool_choice defaulting to "auto". Providers that serve models without tool-call support (e.g. morphllm without --enable-auto-tool-choice) reject the request with a 400 error.
Root cause
In packages/opencode/src/session/prompt.ts, the SessionTools.resolve() call and the toolChoice parameter in handle.process() both ignore model.capabilities.toolcall. Setting tool_call: false in model config correctly sets capabilities.toolcall to false in packages/opencode/src/provider/provider.ts:1216, but the prompt loop never reads that field.
Why this is bad
The user explicitly opts out of tool calls via tool_call: false, expecting a model-safe request with no tools sent. Instead the prompt loop sends a full tools array with tool_choice: "auto", which providers reject if they do not support tool calling at all. There is no workaround at the session or request level -- the config field is effectively a no-op.
Start a session with that model and send a message.
The prompt loop calls the provider API with "tools": [...] and "tool_choice": "auto", even though tool calls are disabled.
Providers that require --enable-auto-tool-choice and --tool-call-parser flags return HTTP 400.
Expected
With tool_call: false, the LLM request body should contain neither tools nor tool_choice keys. The prompt loop should skip SessionTools.resolve() entirely.
This issue might be a duplicate of existing issues. Please check:
在发送请求时检查 capabilities.toolcall,当为 false 时不发送 tools 参数。 #19966: Same bug — tool_call: false is stored in capabilities.toolcall but not checked when resolving and sending tools in the prompt loop, causing providers to receive a tools array even when tool calling is disabled. That issue is now closed; if the fix did not land or regressed, it may be worth referencing it.
Confirming this on opencode 1.17.15 (linuxbrew binary) against NVIDIA's hosted NIM endpoint (integrate.api.nvidia.com/v1, model nvidia/nemotron-3-content-safety). The endpoint rejects any request carrying tools with vLLM's "auto" tool choice requires --enable-auto-tool-choice and --tool-call-parser to be set, and tool_call: false in the model config does not stop opencode from sending them.
Independent confirmation of the root cause described above: grepping the bundled binary shows capabilities.toolcall is computed during config/catalog merge (toolcall:f.tool_call??_?.capabilities.toolcall??!0) but never read anywhere in the request path, so the config field is a no-op for outgoing requests.
Working user-level workaround until this is fixed: define an agent with the legacy tools wildcard map, which does prevent tool definitions from being sent:
Verified working end-to-end with opencode run --agent safety -- the request goes out with no tools and the endpoint responds normally.
Note for anyone trying the recommended permission field instead: permission: { "...": "deny" } does not help here. It blocks tool execution but the tool definitions are still included in the request body, so providers that reject tool-bearing requests still fail.
Agent: Claude Code (Claude Fable 5, model id claude-fable-5)
Date: 2026-07-09
Method: log analysis, binary inspection of the opencode bundle, live API probes against the affected endpoint
Scope: user's local opencode config and logs; no access to opencode source checkout
I hit the same root cause from a different angle — Vertex Gemini image models (gemini-2.5-flash-image) reject every request with Unable to submit request because the model does not support function calling — and filed #41464 before the duplicate bot pointed me here. Same cause you diagnosed: capabilities.toolcall is assigned in provider.ts and never read in the request path.
The patch is a guard in resolveTools (session/llm/request.ts), which is where every send path converges:
if(!input.model.capabilities.toolcall)return{}
Verified by capturing the outgoing provider request from a source build with the stock build agent and no config overrides: a toolcall: false model now sends no tools field at all (was 12 declarations), while a tool-calling model in the same session still sends all 12. Two tests added; I checked the negative one fails without the guard.
Worth noting this is the third report of the same thing — #19966 (closed by the 60-day inactivity bot rather than on merit), this issue, and #41464. @sbadakhc's morphllm/NVIDIA NIM cases should be covered by the same guard, since it drops the tools array before any provider-specific serialization.
Happy to fold #41464 into this issue if maintainers prefer one thread.
Still live on v2.0.10 (channel latest, Linux arm64, stock build agent, no plugins).
This is not limited to a hand-written tool_call: false. It reproduces straight from the models.dev catalog: z-ai/glm-5.2:free and nvidia/nemotron-3.5-content-safety:free both carry tool_call: false, yet OpenCode still sends an edit tool definition. OpenRouter answers with a routing error instead of a completion, so the model is a trap: it is offered in /models, then every tool-using turn fails.
Repro:
opencode run --model openrouter/z-ai/glm-5.2:free "Reply with the single word OK and nothing else."
Error: No endpoints found that support tool use. Try disabling "edit". To learn more about provider routing, visit: https://openrouter.ai/docs/guides/routing/provider-selection
That message comes from OpenRouter's "Filter by Tool Compatibility" routing step, which fires only when the request body carries tools, so it is independent proof the outgoing request ignored capabilities.toolcall: false.
Server log:
level=ERROR message="Failed to drain Session" cause="Session.StepFailedError: No endpoints found that support tool use. Try disabling \"edit\". ..."
Expected: a model with capabilities.toolcall: false should send neither tools nor tool_choice, or should not be offered to a tool-using agent in the first place. Neither guard exists on v2.
The earlier fix (#41463, the guard where tools are resolved) was auto-closed before merge and never reached v2, so the flag is still assigned during catalog/config merge and never read on the send path.
Mitigations in the meantime: disabled: true on the tool-less models, or an agent with tools: { "*": false } as @sbadakhc found.
Still broken on v2.0.32 (Windows x64, latest channel, stock build agent, no plugins) against a tool-less model served by our internal vLLM gateway. Model configured with the current capabilities spelling:
Wire capture shows the agent request still carries the full 12-tool set (edit, glob, grep, question, read, shell, skill, subagent, webfetch, websearch, write, execute) and no tool_choice key at all. OpenAI-compatible servers treat an omitted tool_choice as "auto" — the model is free to pick a tool — and engines with tool calling disabled for a model (like ours, and the other reporters' endpoints) reject any tool-bearing request on exactly that basis with their standard tool-choice 400. Curiously the per-session title request correctly carries no tools and is accepted — only the agent run path regresses. Repro:
opencode run -m ssnc-onprem-us/zai-org-GLM-OCR "Reply with exactly: OK"
Two additional data points not yet in this thread.
1. The tools: { "*": false } mitigation no longer works on 2.0.32 — user-defined agents cannot be used at all. Earlier comments suggest moving the model onto an agent whose tool map disables tools, but on this release such agents cannot be selected: --agent ocr fails with "Agent not found" no matter where the agent is defined (config agents/agent keys, ~/.config/opencode/agents/*.md, project .opencode/agents/*.md). Built-in agents still resolve (--agent build works), so selection itself functions — only user-defined agents fail to load. The agent-defining docs are ahead of the CLI here.
2. Model body nulls are a working per-model workaround on 2.0.32, using null = delete-key merge semantics:
Verified live: the merged agent request goes out with no tools/tool_choice/parallel_tool_calls keys and the engine answers normally. Two gotchas worth recording: a null deletes the key, but "tools": [] does not — the empty array stays in the body and the engine still returns a 400 validation error, so only null works. It also composes cleanly with other body pins since merge order applies variant-last.
Would appreciate the capabilities.toolcall guard (the #41463 approach) landing so the explicit capabilities.tools: false assertion becomes effective — the body-null workaround is a stand-in until then.
Activity
github-actions commentedon Jul 5, 2026
This issue might be a duplicate of existing issues. Please check:
tool_call: falseis stored incapabilities.toolcallbut not checked when resolving and sending tools in the prompt loop, causing providers to receive a tools array even when tool calling is disabled. That issue is now closed; if the fix did not land or regressed, it may be worth referencing it.sbadakhc commentedon Jul 9, 2026
Confirming this on opencode 1.17.15 (linuxbrew binary) against NVIDIA's hosted NIM endpoint (
integrate.api.nvidia.com/v1, modelnvidia/nemotron-3-content-safety). The endpoint rejects any request carrying tools with vLLM's"auto" tool choice requires --enable-auto-tool-choice and --tool-call-parser to be set, andtool_call: falsein the model config does not stop opencode from sending them.Independent confirmation of the root cause described above: grepping the bundled binary shows
capabilities.toolcallis computed during config/catalog merge (toolcall:f.tool_call??_?.capabilities.toolcall??!0) but never read anywhere in the request path, so the config field is a no-op for outgoing requests.Working user-level workaround until this is fixed: define an agent with the legacy
toolswildcard map, which does prevent tool definitions from being sent:{ "agent": { "safety": { "mode": "primary", "model": "nvidia/nvidia/nemotron-3-content-safety", "tools": { "*": false } } } }Verified working end-to-end with
opencode run --agent safety-- the request goes out with no tools and the endpoint responds normally.Note for anyone trying the recommended
permissionfield instead:permission: { "...": "deny" }does not help here. It blocks tool execution but the tool definitions are still included in the request body, so providers that reject tool-bearing requests still fail.IngTian commentedon Aug 10, 2026
There's a fix up for this now: #41463.
I hit the same root cause from a different angle — Vertex Gemini image models (
gemini-2.5-flash-image) reject every request withUnable to submit request because the model does not support function calling— and filed #41464 before the duplicate bot pointed me here. Same cause you diagnosed:capabilities.toolcallis assigned inprovider.tsand never read in the request path.The patch is a guard in
resolveTools(session/llm/request.ts), which is where every send path converges:Verified by capturing the outgoing provider request from a source build with the stock
buildagent and no config overrides: atoolcall: falsemodel now sends notoolsfield at all (was 12 declarations), while a tool-calling model in the same session still sends all 12. Two tests added; I checked the negative one fails without the guard.Worth noting this is the third report of the same thing — #19966 (closed by the 60-day inactivity bot rather than on merit), this issue, and #41464. @sbadakhc's morphllm/NVIDIA NIM cases should be covered by the same guard, since it drops the tools array before any provider-specific serialization.
Happy to fold #41464 into this issue if maintainers prefer one thread.
TohaSlovo commentedon Sep 13, 2026
placeholder
yesvus commentedon Sep 20, 2026
Still live on v2.0.10 (channel latest, Linux arm64, stock
buildagent, no plugins).This is not limited to a hand-written
tool_call: false. It reproduces straight from the models.dev catalog:z-ai/glm-5.2:freeandnvidia/nemotron-3.5-content-safety:freeboth carrytool_call: false, yet OpenCode still sends anedittool definition. OpenRouter answers with a routing error instead of a completion, so the model is a trap: it is offered in/models, then every tool-using turn fails.Repro:
opencode run --model openrouter/z-ai/glm-5.2:free "Reply with the single word OK and nothing else."That message comes from OpenRouter's "Filter by Tool Compatibility" routing step, which fires only when the request body carries tools, so it is independent proof the outgoing request ignored
capabilities.toolcall: false.Server log:
Expected: a model with
capabilities.toolcall: falseshould send neithertoolsnortool_choice, or should not be offered to a tool-using agent in the first place. Neither guard exists on v2.The earlier fix (#41463, the guard where tools are resolved) was auto-closed before merge and never reached v2, so the flag is still assigned during catalog/config merge and never read on the send path.
Mitigations in the meantime:
disabled: trueon the tool-less models, or an agent withtools: { "*": false }as @sbadakhc found.ssc-esiemiat commentedon Oct 18, 2026
Still broken on v2.0.32 (Windows x64, latest channel, stock
buildagent, no plugins) against a tool-less model served by our internal vLLM gateway. Model configured with the currentcapabilitiesspelling:{ "providers": { "ssnc-onprem-us": { "models": { "zai-org-GLM-OCR": { "capabilities": { "tools": false } } } } } }Wire capture shows the agent request still carries the full 12-tool set (
edit,glob,grep,question,read,shell,skill,subagent,webfetch,websearch,write,execute) and notool_choicekey at all. OpenAI-compatible servers treat an omittedtool_choiceas"auto"— the model is free to pick a tool — and engines with tool calling disabled for a model (like ours, and the other reporters' endpoints) reject any tool-bearing request on exactly that basis with their standard tool-choice 400. Curiously the per-session title request correctly carries no tools and is accepted — only the agent run path regresses. Repro:Two additional data points not yet in this thread.
1. The
tools: { "*": false }mitigation no longer works on 2.0.32 — user-defined agents cannot be used at all. Earlier comments suggest moving the model onto an agent whose tool map disables tools, but on this release such agents cannot be selected:--agent ocrfails with "Agent not found" no matter where the agent is defined (configagents/agentkeys,~/.config/opencode/agents/*.md, project.opencode/agents/*.md). Built-in agents still resolve (--agent buildworks), so selection itself functions — only user-defined agents fail to load. The agent-defining docs are ahead of the CLI here.2. Model
bodynulls are a working per-model workaround on 2.0.32, using null = delete-key merge semantics:Verified live: the merged agent request goes out with no
tools/tool_choice/parallel_tool_callskeys and the engine answers normally. Two gotchas worth recording: a null deletes the key, but"tools": []does not — the empty array stays in the body and the engine still returns a 400 validation error, so only null works. It also composes cleanly with otherbodypins since merge order applies variant-last.Would appreciate the
capabilities.toolcallguard (the #41463 approach) landing so the explicitcapabilities.tools: falseassertion becomes effective — the body-null workaround is a stand-in until then.