When launching Codex with an Ollama model (e.g. glm-5.1:cloud via --local-provider ollama), the session correctly uses the Ollama endpoint (http://localhost:11434/v1). However, when switching back to a cloud model like gpt-5.4 mid-session (via /model or model picker), the requests still go to the Ollama endpoint instead of the default OpenAI endpoint (https://api.openai.com/v1). This causes request failures because gpt-5.4 doesn't exist on the Ollama server.
Steps to reproduce
- Launch
codex --model glm-5.1:cloud --local-provider ollama
- Confirm it works (requests hit
localhost:11434)
- Switch model to
gpt-5.4 via the model picker
- Send a prompt — requests still go to
localhost:11434 and fail
Expected behavior
When switching to a model that isn't available on the current provider, the system should either automatically resolve the correct provider, or at minimum warn the user.
Root cause analysis
The bug is in how OverrideTurnContext handles model switching in the session layer:
-
Startup: Config::from_config() resolves model_provider_id (e.g. "ollama") and looks up the corresponding ModelProviderInfo from model_providers map, which has base_url: Some("http://localhost:11434/v1"). This gets stored in SessionConfiguration.provider.
-
Model switch: When the user picks a new model, the TUI sends Op::OverrideTurnContext { model: Some("gpt-5.4"), ... }. The handler converts this into a CollaborationMode update and calls SessionConfiguration.apply(&updates).
-
SessionConfiguration.apply() (codex-rs/core/src/session/session.rs:110) updates collaboration_mode (which holds the model name) but never touches provider. The provider: ModelProviderInfo field stays set to Ollama.
-
New turn creation: new_turn_from_configuration() passes session_configuration.provider.clone() to make_turn_context(), which creates a ModelProvider from the stale Ollama provider info.
-
TurnContext.with_model() (codex-rs/core/src/session/turn_context.rs:97) similarly clones self.provider without resolving a new one.
The OverrideTurnContext protocol message (codex-rs/protocol/src/protocol.rs:505) only carries model: Option<String> — there is no model_provider_id field to communicate a provider change.
Proposed fix
Option A (recommended): Auto-resolve provider on model change
In SessionConfiguration.apply() (or update_settings()), after updating collaboration_mode.model, re-resolve the provider:
- If the model is available in the remote model catalog → switch to the
openai provider
- If the model slug matches a known OpenAI prefix (gpt-, o1-, o3-, codex-) → switch to
openai
- Otherwise → keep the current provider (Ollama/LMStudio)
This requires passing Config.model_providers into SessionConfiguration.apply(), which already has access via original_config_do_not_use.
Option B: Add model_provider_id to OverrideTurnContext
Add a model_provider_id: Option<String> field to the protocol message so the TUI can explicitly specify which provider to use when switching models. This requires the model picker UI to know which provider each model belongs to.
Option C: Resolve in new_turn_from_configuration
In Session::new_turn_from_configuration(), after resolving model_info, check if the current provider is appropriate for the model and switch if needed.
Related issues: #15219 (thread/resume doesn't restore model_provider), #17541 (model switch fails with encrypted content error), #17261 (feat: /model support for LM Studio and Ollama), #8319 (/model support for ollama/lmstudio)
When launching Codex with an Ollama model (e.g.
glm-5.1:cloudvia--local-provider ollama), the session correctly uses the Ollama endpoint (http://localhost:11434/v1). However, when switching back to a cloud model likegpt-5.4mid-session (via/modelor model picker), the requests still go to the Ollama endpoint instead of the default OpenAI endpoint (https://api.openai.com/v1). This causes request failures becausegpt-5.4doesn't exist on the Ollama server.Steps to reproduce
codex --model glm-5.1:cloud --local-provider ollamalocalhost:11434)gpt-5.4via the model pickerlocalhost:11434and failExpected behavior
When switching to a model that isn't available on the current provider, the system should either automatically resolve the correct provider, or at minimum warn the user.
Root cause analysis
The bug is in how
OverrideTurnContexthandles model switching in the session layer:Startup:
Config::from_config()resolvesmodel_provider_id(e.g."ollama") and looks up the correspondingModelProviderInfofrommodel_providersmap, which hasbase_url: Some("http://localhost:11434/v1"). This gets stored inSessionConfiguration.provider.Model switch: When the user picks a new model, the TUI sends
Op::OverrideTurnContext { model: Some("gpt-5.4"), ... }. The handler converts this into aCollaborationModeupdate and callsSessionConfiguration.apply(&updates).SessionConfiguration.apply()(codex-rs/core/src/session/session.rs:110) updatescollaboration_mode(which holds the model name) but never touchesprovider. Theprovider: ModelProviderInfofield stays set to Ollama.New turn creation:
new_turn_from_configuration()passessession_configuration.provider.clone()tomake_turn_context(), which creates aModelProviderfrom the stale Ollama provider info.TurnContext.with_model()(codex-rs/core/src/session/turn_context.rs:97) similarly clonesself.providerwithout resolving a new one.The
OverrideTurnContextprotocol message (codex-rs/protocol/src/protocol.rs:505) only carriesmodel: Option<String>— there is nomodel_provider_idfield to communicate a provider change.Proposed fix
Option A (recommended): Auto-resolve provider on model change
In
SessionConfiguration.apply()(orupdate_settings()), after updatingcollaboration_mode.model, re-resolve the provider:openaiprovideropenaiThis requires passing
Config.model_providersintoSessionConfiguration.apply(), which already has access viaoriginal_config_do_not_use.Option B: Add
model_provider_idtoOverrideTurnContextAdd a
model_provider_id: Option<String>field to the protocol message so the TUI can explicitly specify which provider to use when switching models. This requires the model picker UI to know which provider each model belongs to.Option C: Resolve in
new_turn_from_configurationIn
Session::new_turn_from_configuration(), after resolving model_info, check if the current provider is appropriate for the model and switch if needed.Related issues: #15219 (thread/resume doesn't restore model_provider), #17541 (model switch fails with encrypted content error), #17261 (feat: /model support for LM Studio and Ollama), #8319 (
/modelsupport for ollama/lmstudio)