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
Mid-session model resolution silently falls back to global config.model, ignoring the TUI-selected model (selected model receives zero traffic) #47968
When a user selects a model via the TUI (e.g. Big Pickle via Zen), the interface confirms the selection — but zero requests ever reach that model's provider. All actual traffic is silently routed to whatever model is set in the global config.model field, regardless of the TUI selection.
Observed
TUI shows "Big Pickle" as the active model for the entire session.
The Zen activity dashboard (provider of Big Pickle, opencode/big-pickle) shows zero requests for today — the last recorded usage was yesterday.
A third-party billing dashboard for a different, custom provider (custom-provider/deepseek-v4-flash-0731 — the value currently in config.model) shows 87 primary-agent turns with this exact same session id, including large context windows (29k→188k tokens), confirming these are real conversation turns — not subagent calls.
The local opencode log (message=stream) confirms both providers appeared for the same session: early turns → Zen (Big Pickle), later turns → custom-provider (deepseek-v4-flash-0731) — all while TUI continued showing Big Pickle.
Zen dashboard shows last usage = yesterday. Custom provider dashboard shows 87 primary turns today for the same session. Zero traffic reached Big Pickle today despite the TUI showing it as selected.
Expected
When the user selects Big Pickle in the TUI, all primary-agent requests should be sent to the Zen provider (opencode/big-pickle). Fallbacks to the global config.model should only occur when no model is explicitly selected.
Actual
The engine ignores the TUI-selected model and routes requests to config.model (a different provider/model entirely). The TUI continues to display the selected model, giving no indication that traffic is going elsewhere. There is no error, no warning, no log entry indicating the fallback happened.
The user had explicitly selected Big Pickle in the TUI, but config.model silently overrides it.
Workaround
Set config.model to the same model you actually use in the TUI, so any fallback lands on the intended provider. This defeats the purpose of the model picker but prevents silent misrouting.
If your case occurs independently of subagent activity (i.e. a plain single-agent session with no background subagents), it may be a distinct root cause and worth keeping open separately.
The third-party-billing-dashboard evidence is the strongest part of this report — it turns "TUI shows the wrong model" into "N paid turns on a provider the user did not pick this morning," which is a different severity class. A few things that help whoever picks this up:
The dup link (#38770) is the same failure family but a narrower trigger.#38770 reverts the selection when a background subagent notification is injected, and the system prompt even keeps reporting the stale model identity afterwards. Here the flip happens mid-session with no visible injection point and, per the log excerpt, early turns went to Zen and later turns to config.model. What both share is the invariant being violated: a session-scoped explicit selection must dominate config.model for every subsequent turn, and any deviation must be observable. Right now the resolution order is only enforced at turn start, and nothing re-asserts it after mid-session events (notification frames, compaction, provider error/retry, session resume).
A resolution-order invariant worth writing down (this is the generalized version of the workaround at the bottom of the report):
config.model may only fill the empty slot — it is a default, not an override. Any code path reading config.model while an explicit selection is live is a bug, regardless of which event triggered the re-resolution.
Resolved-model echo per turn: the turn's stream metadata (the same payload that already carries provider/model for billing) should be surfaced in the TUI status line. The reporter could then have seen deepseek-v4-flash-0731 on the status line at turn N instead of discovering it from a third-party dashboard. This is the same "echo the resolved value, never the requested one" rule that applies to model-id normalization in proxies — the client-side version is "echo the resolved selection, never the TUI label."
Regression test sketch (all in one fixture, since each step alone can pass while the sequence fails):
Select model A explicitly in a session with config.model = B → assert turn 1 targets A.
Force a mid-session re-resolution event (compaction or a provider error/retry on A) → assert the re-resolution re-reads the session selection, not config.model.
Resume the persisted session → assert the selection survives resume.
Assert the stream metadata for every turn reports the actually-dispatched model.
Step 3 is the one most likely to reproduce the mid-session flip here, because a provider error on the selected model is exactly the kind of event that would push the resolver back to defaults if the fallback path doesn't re-consult the session selection first.
The workaround from the report (set config.model to the picked model) is correct as a stopgap, with one caveat: it also silently changes the model for any other session or agent that relies on the default — so it's fine for a single-purpose repo, but multi-project users should apply it per-project in opencode.json rather than globally.
Activity
github-actions commentedon Sep 8, 2026
This issue might be a duplicate of existing issues. Please check:
If your case occurs independently of subagent activity (i.e. a plain single-agent session with no background subagents), it may be a distinct root cause and worth keeping open separately.
seven7763 commentedon Sep 8, 2026
The third-party-billing-dashboard evidence is the strongest part of this report — it turns "TUI shows the wrong model" into "N paid turns on a provider the user did not pick this morning," which is a different severity class. A few things that help whoever picks this up:
The dup link (#38770) is the same failure family but a narrower trigger. #38770 reverts the selection when a background subagent notification is injected, and the system prompt even keeps reporting the stale model identity afterwards. Here the flip happens mid-session with no visible injection point and, per the log excerpt, early turns went to Zen and later turns to
config.model. What both share is the invariant being violated: a session-scoped explicit selection must dominateconfig.modelfor every subsequent turn, and any deviation must be observable. Right now the resolution order is only enforced at turn start, and nothing re-asserts it after mid-session events (notification frames, compaction, provider error/retry, session resume).A resolution-order invariant worth writing down (this is the generalized version of the workaround at the bottom of the report):
with two corollaries:
config.modelmay only fill the empty slot — it is a default, not an override. Any code path readingconfig.modelwhile an explicit selection is live is a bug, regardless of which event triggered the re-resolution.deepseek-v4-flash-0731on the status line at turn N instead of discovering it from a third-party dashboard. This is the same "echo the resolved value, never the requested one" rule that applies to model-id normalization in proxies — the client-side version is "echo the resolved selection, never the TUI label."Regression test sketch (all in one fixture, since each step alone can pass while the sequence fails):
config.model= B → assert turn 1 targets A.config.model.Step 3 is the one most likely to reproduce the mid-session flip here, because a provider error on the selected model is exactly the kind of event that would push the resolver back to defaults if the fallback path doesn't re-consult the session selection first.
The workaround from the report (set
config.modelto the picked model) is correct as a stopgap, with one caveat: it also silently changes the model for any other session or agent that relies on the default — so it's fine for a single-purpose repo, but multi-project users should apply it per-project inopencode.jsonrather than globally.thdxr commentedon Oct 11, 2026
Thanks for the report! OpenCode 2.0 is now out and development is focused on V2, so we're closing issues filed against OpenCode 1. Please upgrade (
opencode upgrade --major, details: https://anoma.ly/notes/opencode-2-0), and if this is still relevant on 2.0, reopen this issue or open a new one with your 2.0 version.— from 𝕺𝖕𝖊𝖓𝕮𝖔𝖉𝖊