Skip to content

TUI footer shows stale default provider/model after switching via /models #23741

Closed

Description

@coygeek

Description

After switching models/providers from the TUI using /models, OpenCode sends the prompt to the newly selected provider correctly, but the footer/input status bar still shows the original default provider/model instead of the active one.

This appears to be a UI state bug: the actual request uses the selected provider, but the bottom bar is not updated.

Steps to reproduce

  1. Start OpenCode normally from the terminal:
    opencode
  2. Make sure the default model/provider is something else first (for example GPT-5.4 via GitHub Copilot).
  3. Open the model picker with /models.
  4. Switch to another provider/model, for example:
    • Kimi K2.6
    • GLM 5.1 / Z-AI Coding Plan
  5. Send a message.

Actual behavior

  • The response is generated by the newly selected provider/model.
  • The conversation output also reflects the new provider/model.
  • But the footer/input status bar at the bottom still shows the old default provider/model (for example GPT-5.4 GitHub Copilot) instead of the currently active one.

Expected behavior

The footer/input status bar should always reflect the active session model/provider after a /models switch.

Notes

  • I reproduced this with multiple providers, not just Kimi.
  • Example pattern:
    • Default provider shown before switch: GPT-5.4 GitHub Copilot
    • Switched provider actually used: Kimi K2.6
    • Footer still incorrectly shows: GPT-5.4 GitHub Copilot

This started happening within roughly the last week.

Environment

  • OpenCode version: 1.14.28 (from the screenshot)
  • Interface: terminal/TUI
  • Default provider: GitHub Copilot (GPT-5.4)
  • Reproduced with:
    • Kimi
    • Z-AI Coding Plan / GLM 5.1

Activity

  1. added
    opentuiThis relates to changes in v1.0, now that opencode uses opentui
    on Apr 21, 2026
  2. github-actions commented on Apr 21, 2026

    @github-actions
    Contributor

    This issue might be a duplicate of existing issues. Please check:

  3. coygeek commented on Apr 27, 2026

    @coygeek
    Author

    Still not fixed

  4. coygeek commented on Apr 27, 2026

    @coygeek
    Author

    This is so annoying.

    Steps to reproduce:

    1. Launch opencode with an alias 'minimax'
      opencode -m minimax-coding-plan/MiniMax-M2.7
    2. This is what appears on screen:
    Image 3. Send a message, and this is what appears on screen: Image 4. Notice that the model is routing to the wrong provider (since my limit of Github Copilot has ran out). Its clearly using/routing the wrong model. It should be Minimax, but its trying to use GPT-5.4 (since its my default model in OpenCode): Image

    To get this session back to normal, i now have to do the following workaround:

    1. Press ESC on the keyboard.
    2. type /model, switch to minimax
    3. copy the previous message, paste it again, now it proceeds (see below)
    Image

    This is a major glitch.

  5. coygeek commented on Apr 27, 2026

    @coygeek
    Author

    Issue #23741: Model Reverts to Default After First Message When Launched with -m

    Summary

    When launching OpenCode with a specific model via opencode -m <model>, the model shown in the status bar appears correct initially, but after the first message completes (or even before the first message, depending on bootstrap timing), it silently reverts to the user's default/agent-configured model.

    Root cause: A createEffect in local.tsx synchronizes the agent's configured model into the per-agent model store, which overwrites the CLI -m override. The CLI model lives at Priority 3 (fallback), but the effect promotes the agent model to Priority 1 (explicit store), effectively discarding the CLI override.

    Reproduction

    opencode -m minimax-coding-plan/MiniMax-M2.7
    1. The status bar initially shows MiniMax-M2.7
    2. Send a message or wait for bootstrap to complete
    3. The status bar and actual model routing switches to the default model (e.g., GPT-5.4)
    4. User must press ESC, type /model, re-select the intended model, and resend the message

    Root Cause Analysis

    Key files and line numbers

    File Lines Role
    packages/opencode/src/cli/cmd/tui/context/local.tsx 401-417 Root cause: createEffect that overwrites CLI model
    packages/opencode/src/cli/cmd/tui/context/local.tsx 200-209 currentModel memo with three-tier priority
    packages/opencode/src/cli/cmd/tui/context/local.tsx 161-198 fallbackModel memo (includes CLI args.model)
    packages/opencode/src/cli/cmd/tui/context/local.tsx 289-312 model.set() — writes under modelStore.model[agent.name]
    packages/opencode/src/cli/cmd/tui/app.tsx 331-343 onMount where CLI -m model is stored via local.model.set()
    packages/opencode/src/cli/cmd/tui/context/local.tsx 64-66 agent.current() — returns agents().find(...) ?? agents().at(0)

    Detailed mechanism

    The model resolution uses a three-tier priority chain in currentModel() (lines 200-209):

    getFirstValidModel(
      () => modelStore.model[agent.name],   // PRIORITY 1: explicit store
      () => agent.model,                     // PRIORITY 2: agent's configured model
      fallbackModel,                         // PRIORITY 3: CLI args, config, recent, default
    )
    

    The CLI -m model lives in fallbackModel (Priority 3). The buggy createEffect (lines 401-417) promotes the agent's configured model to Priority 1:

    createEffect(() => {
      const value = agent.current()
      if (!value) return
      if (value.model) {
        if (isModelValid(value.model))
          model.set({                          // <-- WRITES TO modelStore.model[agent.name]
            providerID: value.model.providerID,
            modelID: value.model.modelID,
          })
        else
          toast.show({ ... })
      }
    })

    Race condition timeline

    1. TUI starts. sync.data.agent is initially [] (empty). agent.current() returns undefined. The createEffect guard if (!value) return prevents it from firing.

    2. app.tsx onMount fires → local.model.set(cli_model, { recent: true }) → stores CLI model under modelStore.model[currentAgentName].

    3. sync.bootstrap() (async) completes and populates sync.data.agent with the real agent list.

    4. SolidJS reactivity: agents() memo changes → agent.current() re-evaluates → now returns the actual agent object.

    5. The createEffect fires. If the current agent has a .model property (user-configured in opencode.json), it calls model.set(agent.model), which overwrites modelStore.model[currentAgentName].

    6. currentModel now returns the agent's model (Priority 1 wins) instead of the CLI model (Priority 3 fallback).

    7. All subsequent TUI prompt submissions use the wrong model.

    Why it varies

    • Default build/plan agents have no .model property by default → the createEffect's if (value.model) guard prevents overwrite → bug does NOT manifest.
    • User-configured agents (or when default_agent is set to a custom agent with a model) → the agent has .model → overwrite happens.
    • Timing-dependent: If bootstrap completes before the user sends the first message, even message 1 uses the wrong model. If bootstrap completes after, message 1 is correct but subsequent messages revert.

    Related Issues

    This is the same root cause affecting these linked issues:

    Issue Symptom How it maps to same root cause
    #23741 (this) -m flag model reverts after first message createEffect overwrites CLI model after bootstrap
    #23666 UI model picker resets after first message Same effect fires on sync.data.agent refresh post-message completion
    #21351 Model resets when switching Plan/Build modes Agent switch triggers agent.current() change → effect fires
    #23369 Model reverts on session resume Session resume re-triggers bootstrap/sync → effect fires
    #6636 Subagent model bleeds into main agent Same modelStore.model[agentName] key collision

    Unifying invariant: Each agent in a session must have its own persistent selectedModel state, independent of ag.model, that survives first-message completion, mode switches, subagent dispatch, and session resume. Currently, the createEffect at local.tsx:401 systematically violates this by overwriting user-selected state with agent config on every reactive refresh.

    Fix Direction

    The createEffect at local.tsx:401-417 should NOT overwrite the per-agent model store when the current model was explicitly set by the user (via -m flag, /model command, or UI picker). Proposed approaches:

    1. Guard with an "explicitly set" flag: Add a flag like modelStore.override[agentName] that is set to true when the user explicitly sets a model (via -m, /model, or UI). The createEffect checks this flag before overwriting:

      createEffect(() => {
        const value = agent.current()
        if (!value) return
        if (value.model && !modelStore.override[value.name]) {  // <-- guard
          model.set({ ... })
        }
      })
    2. Separate "agent default" vs "user selection" stores: Instead of a single modelStore.model, maintain two separate stores and adjust the priority chain in currentModel.

    3. Remove the effect entirely: Rely on the existing priority chain where a.model is already at Priority 2 in currentModel. The effect is duplicative — it copies agent.model (Priority 2) into modelStore.model (Priority 1), which is what causes the Priority 1 override of the user's CLI choice.

    Option 3 is the simplest and most likely correct fix. The createEffect exists so that when a user switches agents, the model automatically updates to the new agent's configured model. But this can be handled differently — for example, by clearing the per-agent store on agent switch rather than proactively writing the agent model into it.

    Additional consideration: fallbackModel does not help

    Even though fallbackModel checks args.model first (line 162), it is Priority 3 in the chain. The createEffect puts the agent model at Priority 1, making fallbackModel unreachable when the agent has a model.

    Verification

    To verify a fix:

    1. Launch with -m flag specifying a model different from the agent's configured model
    2. Verify the status bar shows the -m model before and after bootstrap
    3. Send a message and confirm the model does not revert
    4. Switch between agents and verify the -m model persists (or intentional agent-model switch behavior applies)
  6. removed
    opentuiThis relates to changes in v1.0, now that opencode uses opentui
    on May 3, 2026
  7. coygeek commented on May 23, 2026

    @coygeek
    Author

    Fixed

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