Skip to content

Mid-session model resolution silently falls back to global config.model, ignoring the TUI-selected model (selected model receives zero traffic) #47968

Closed as not planned

Description

@a-d-gordienko

Environment

opencode v1.18.29

Summary

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.

Timeline (same session, one day)

providerID=Zen      modelID=big-pickle     251 turns
providerID=custom   modelID=v4-flash-0731   87 turns (primary agent=build)

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.

Config (anonymized)

{
  "model": "custom-provider/deepseek-v4-flash-0731",   // <-- all traffic goes here
  "small_model": "custom-provider/deepseek-v4-flash"
}

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.

Related

Activity

  1. github-actions commented on Sep 8, 2026

    @github-actions
    Contributor

    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.

  2. seven7763 commented on Sep 8, 2026

    @seven7763

    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):

    effective_model = explicit_session_selection
                    ?? agent_config.model
                    ?? config.model
    

    with two corollaries:

    1. 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.
    2. 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):

    1. Select model A explicitly in a session with config.model = B → assert turn 1 targets A.
    2. Inject a background subagent notification (the Background subagent notification silently reverts manually-selected model to config default #38770 trigger) → assert turn 2 still targets A.
    3. 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.
    4. Resume the persisted session → assert the selection survives resume.
    5. 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.

  3. thdxr commented on Oct 11, 2026

    @thdxr
    Member

    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 𝕺𝖕𝖊𝖓𝕮𝖔𝖉𝖊

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions