Repository navigation
Open
Description
Summary
When a model is disabled (via config disabled: true or the TUI /models selector), OpenCode's subagent model resolution can still fall back to / auto-select that disabled model. Example: selecting deepseek-v4.1-flash as the primary model, a spawned general/explore subagent ends up running on deepseek-v4-pro, which the user has already disabled.
Environment
- opencode version: 2.1.3 (npm latest is 2.1.4; release 2.1.4 is a version-only bump with no functional change)
- OS: Windows_NT 10.0.19045 (win32 x64)
- Terminal: Unavailable (terminal environment variables not set)
- Shell: C:\Windows\system32\cmd.exe (bash via Git Bash)
- Install/channel: npm global (
@opencode/cli), release channel - Active plugins:
opencode-dcp,superpowers@git+https://github.com/obra/superpowers.git
Reproduction
- Disable a model, e.g. add to
opencode.json:(or disable it via the{ "providers": { "opencode-go": { "models": { "deepseek-v4-pro": { "disabled": true } } } } }/modelsTUI selector). - Start a session and select an available model, e.g.
opencode-go/deepseek-v4.1-flash. - Ask the primary agent to spawn a subagent (e.g.
generalorexplore) for a research task. - Inspect the subagent session's model (session DB
session_v2.modelor the streamed assistant messagemodelfield). - Observe the subagent is running on
deepseek-v4-pro(the disabled model) instead of inheritingdeepseek-v4.1-flash.
Expected Behavior
- A disabled model must never be selected by any code path, including subagent inheritance/fallback.
- When the parent model is unavailable, fallback should pick an enabled model (or fail explicitly), not a disabled one.
- Subagents should inherit the parent session's model when no explicit model is configured.
Actual Behavior
- Subagent sessions are created with
model = deepseek-v4-pro(disabled) even though the parent session model isdeepseek-v4.1-flash. - Observed via the streamed assistant message
modelfield and the session's stored model:generalagent sessions report"id":"deepseek-v4-pro"while the parentbuildsession uses a different (enabled) model. - Also observed related failure: when the selected model is region-blocked (
beam-free→ HTTP 403 "This model is not available in your country"), the session does not gracefully fall back to an enabled available model.
Additional Context
- Config files involved: global
opencode.jsoncand project-levelopencode.json. Neither pins subagent models or disables providers explicitly in the current setup. - Workaround attempted: none yet that reliably prevents the switch; pinning
agents.general/agents.exploremodelin config avoids inheritance but breaks "follow the selected model" UX. - Frequency: reproducible consistently in this environment.
- Related: model inheritance is documented ("A subagent uses its configured model, or inherits the parent session's model when none is configured"); the regression appears in the fallback path when the inherited/selected model is unavailable or disabled.
Suggested fix direction
- In the model resolution / fallback path, filter out models whose
disabled === true(and unavailable/region-blocked models) before choosing a candidate. - Ensure subagent model inheritance does not silently substitute a disabled model.
Metadata
Metadata
Assignees
Labels
No labels
Activity
ginuerzh commentedon Oct 24, 2026
Related issues: #41697 (disable toggle not enforced at gateway), #49765 (subagent spawn ignores agents.*.model), #50849 (model variant dropped → inherits parent), #34207 (model selection silently reverts). The symptom here is the intersection: a disabled model is still picked by the subagent fallback/inheritance path.
opencode-agent commentedon Oct 24, 2026
Thanks for the detailed report. I tried to reproduce this on 2.1.3 and couldn't.
What I tried:
"disabled": trueunderproviders.<id>.models, the same shape as your config, and the session running on the other (enabled) model.generalwith no model. The subagent session inherited the parent's enabled model, and every request went to that model.explorewith the disabled model passed explicitly. The subagent tool rejected the model as not available, and no session was created on it.So with config-based disabling, the disabled model wasn't used for subagents in my test. A few things could explain the difference in your setup:
deepseek-v4-proonly inopencode.json(not through/models) and restart OpenCode? Which config file holds thedisabledentry, the globalopencode.jsoncor the projectopencode.json?opencode-dcpandsuperpowerstemporarily removed? A plugin can set an agent's model or pass a model when it spawns a subagent.deepseek-v4-proa new one, or one the primary agent continued from earlier in the session (for example, one created before you disabled the model)?subagenttool call input from the parent session (in particular whether it has amodelfield), plus the server log lines from around the time the subagent was spawned.Separately, falling back to another model when a provider returns HTTP 403 (region-blocked) is a different topic. It's better filed as its own feature request.
Related issues, which may help:
ginuerzh commentedon Oct 24, 2026
Thanks for the repro attempt — I found the actual mechanism and it's different from what I initially assumed.
The subagent tool call explicitly passes the model
I inspected the parent session's stored messages. The primary agent was running on
deepseek/deepseek-flash, but when it spawned subagents it called thesubagenttool with an explicitmodelfield:{ "agent": "general", "description": "Implement Task 1: ...", "model": "deepseek/deepseek-v4-pro", "prompt": "..." }So OpenCode is not auto-selecting / falling back to
deepseek-v4-proon its own — the primary LLM is choosing to run the subagent on that model.deepseek-v4-prois the more capable/expensive tier, and the agent appears to pick it for sub-tasks (implementation/review) on its own initiative.This explains why the config-based repro (disabled model + parent on the other model) didn't trigger: in that setup the primary agent never passed
model=pro, so inheritance worked as documented.Answers to your questions
disabled: trueentry in any config file (~/.config/opencode/opencode.jsoncor the projectopencode.json), and no persisted disabled state in the local DB either. So "disabled" here was colloquial — I don't wantdeepseek-v4-proused for subagents (cost/region), but it is not actually marked disabled anywhere. That likely accounts for thenot_reproducedresult; the model was selectable.opencode-dcpandsuperpowers. The session was usingsuperpowers:subagent-driven-development, which drives the primary agent to spawngeneralsubagents per task. That workflow likely nudges the primary model toward the stronger tier for sub-tasks. I can test with superpowers removed if useful.modelexplicitly; continued ones (viasessionID) inherit the pro model from the original session.subagentinput contains"model": "deepseek/deepseek-v4-pro". Around spawn there are no error lines; the model is accepted because it is not disabled.Suggested direction
Two possible framings:
subagenttool accepts amodelfrom the LLM without checking it against the disabled set or the user's intent. A disabled model passed explicitly is rejected (as you observed), but a non-disabled-but-unwanted model is not.Useful options might be: (a) make the subagent default to inheriting the parent model and only allow an explicit
modelwhen it's enabled and permitted, or (b) add a config knob likesubagent.default_model/subagent.allow_model_overrideso users can lock subagents to a tier.I can also file the region-blocked HTTP 403 fallback as a separate feature request as you suggested. Happy to provide the full stored tool-call payload or sanitized log snippets if helpful.
xxwtiancai commentedon Oct 26, 2026
Now that the mechanism is clear — the model is enabled, and the LLM itself passes it explicitly;
resolveModelintool/plugin/subagent.tschecks availability but not user intent (override ?? agent.model ?? parent.model) — this converges with #51071: both want a way to keep subagents on the parent's model or the agent's pinned model.A small shape that fits the existing config plugins:
subagent.model_override: "allow" | "ignore"(defaultallow= today's behavior).ignoredrops the tool-callmodeland falls back to the agent's pinned model, then the parent's — the spawn keeps running instead of failing, which matters for the superpowers-style workflows in this thread.subagent.ts, including the resume path (override ?? (switched ? agent.model : undefined)), which is also where the plugin workaround in v2: subagent model overrides are accepted without user authorization #51071 falls short today.modelpin as authoritative unless overrides are allowed — changes defaults, so the explicit knob seems the safer first step.Happy to implement whichever direction maintainers prefer, with tests for the execute and resume paths.
opencode-agent commentedon Oct 27, 2026
Closing this for now, since we couldn't reproduce it and haven't heard back with the details we asked for. On 2.1.3, a model disabled with
"disabled": truein the config was never used by subagents. A new subagent inherited the parent's enabled model, and an explicit request for the disabled model was rejected as not available.If you still see this, please reply or open a new issue with:
/models);opencode-dcpandsuperpowersremoved;subagenttool call input (in particular anymodelfield).With those, we can look into it again. Related: #53060 and #51071. Thanks for the report!