Skip to content

Manually selected model in UI is overridden by agent config model via runtime-fallback #5816

Description

@alferd0806

Bug Description

When a user manually selects a model in the OpenCode UI (using the model selector dropdown) and sends a message, the model is immediately overridden back to the OMO agent-configured model (from oh-my-openagent.jsonagents.sisyphus.model). The manually selected model is used for the message, but the session reverts to the agent config model.

Root Cause

The bug involves two interacting code paths in the runtime-fallback handler:

1. handleSessionCreated unconditionally overwrites session state

In runtime-fallback/event-handler.ts, handleSessionCreated (around line 111688) always overwrites sessionStates[sessionID] with a new state derived from the session.created event info, without checking if the user has already made a manual model selection.

// Simplified logic:
const sessionModel = sessionInfo.model;            // e.g., "B" (user selected)
const preferredModel = resolvePreferredSessionModel(...); // "A" (agent config)
const fallbackIndex = preferredModel !== model
  ? getFallbackModelsForSession(...).indexOf(model) : -1;

const state = createFallbackState(
  fallbackIndex >= 0 && preferredModel ? preferredModel : model
);
// If model B is in the fallback list:
if (fallbackIndex >= 0) {
  state.currentModel = model;      // B
  state.originalModel = preferredModel; // A
}
sessionStates.set(sessionID, state);

Since the user-selected model B is in the agent's fallback_models list, the handler creates a state where:

  • originalModel = A (agent config model)
  • currentModel = B (user-selected, but treated as "fallback")

2. createChatMessageHandler2 interprets the state as an active fallback

In runtime-fallback/chat-message-handler.ts, the chat.message handler (around line 111305) processes each message and at lines 111348-111364:

const activeModel = state.currentModel;  // B
if (activeModel === state.originalModel) return;  // B !== A → continues

// ❌ Applies "fallback override" — actually reverts to agent config!
output.message.model = {
  providerID: parts[0],
  modelID: parts.slice(1).join("/")
};  // Forces model back to A

The handler interprets currentModel !== originalModel as "a fallback is active" and overrides output.message.model with the agent config model — even when the user manually selected the current model.

Event timing scenario

1. User selects model B in UI, sends message
2. session.created fires → handleSessionCreated
   → state = { originalModel: A, currentModel: B }
   → B is treated as "fallback" because it's in fallback_models list
3. chat.message fires → runtime-fallback handler
   → input.model = B, state.currentModel = B
   → requestedModel === state.currentModel → no "manual change" detected
   → activeModel (B) !== originalModel (A) → treats as fallback override
   → output.message.model = A ← BUG!

Expected Behavior

When a user manually selects a model in the UI:

  1. The session state should respect the user's choice, not treat it as a fallback
  2. handleSessionCreated should not overwrite session state that was set by a user's manual model selection
  3. createChatMessageHandler2 should distinguish between "fallback due to error" and "user manually selected a different model"

Suggested Fix

There are two approaches:

Approach A: Fix handleSessionCreated to not overwrite on manual selection

In the session.created event handler, check if the user already has a pending model selection before overwriting. If chat.message already processed the message and detected a manual model change (reset state), session.created should not overwrite that.

Approach B: Fix createChatMessageHandler2 to respect user selection

When activeModel !== originalModel:

  1. Check if input.model contains the user's UI selection
  2. If the "fallback" model matches the user's UI choice, it is NOT a fallback — it is a user preference
  3. Only apply the model override when the model change was due to an API error, not a user action

A minimal fix would be in createChatMessageHandler2: add a check that if input.model matches state.currentModel and differs from state.originalModel, the user intentionally chose this model — do not override.

// Additional check before applying fallback override:
if (input.model) {
  const requestedModel = `${input.model.providerID}/${input.model.modelID}`;
  if (requestedModel === state.currentModel && requestedModel !== state.originalModel) {
    // User intentionally selected this model — not a fallback
    return;
  }
}

Additional Context

  • OMO version: 4.15.0
  • The bug occurs regardless of which models are involved, as long as the UI-selected model is in the agent's fallback_models list
  • The bug does NOT occur for agents without explicit model config (because hasExplicitAgentModelOverride returns false, causing a different code path that does not interact with session state)
  • Discovered while investigating issue Runtime-fallback doesn't trigger on "Free usage exceeded" error — missing patterns in classifier #5815 (runtime-fallback not triggering on Free usage exceeded)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions