Model selector not available with custom LLM provider
What version of the Codex App are you using (From “About Codex” dialog)?
Version 26.317.21539 (1088)
What subscription do you have?
Pro, but I'm using this through 3rd party provider
What platform is your computer?
Darwin 25.3.0 arm64 arm
What issue are you seeing?
The model selector does not appear in the desktop app, even though it correctly appears in the CLI. Interestingly, the Automations tab of the desktop app, does correctly show the model and reasoning level selector, yet the main threads and conversations do not.
I've tried the following without any luck:
- Not pinning any specific models in the configuration
- Using a custom model_catalog.json which is a copy of what ships with codex out of the box, but switched to it refers to models from my own provider
What steps can reproduce the bug?
Use the attached config file:
personality = "pragmatic"
approval_policy = "never"
sandbox_mode = "danger-full-access"
model_provider = "oca"
web_search = "live"
preferred_auth_method = "apikey"
forced_login_methad = "api"
trust_level = "trusted"
# model = "gpt-5.4"
# model_reasoning_effort = "medium"
model_catalog_json = "/Users/core/.codex/model_catalog.json"
model = "oca/gpt-5.4"
model_reasoning_effort = "xhigh"
# model = "oca/gpt-5.4"
# model_reasoning_effort = "xhigh"
[model_providers.oca]
base_url = "https://code-internal.aiservice.us-chicago-1.oci.oraclecloud.com/20250206/app/litellm"
http_headers = { "client" = "codex-cli", "client-version" = "0" }
name = "Oracle Code Assist"
wire_api = "responses"
[projects."/Users/core"]
trust_level = "trusted"
[features]
multi_agent = true
[notice.model_migrations]
"gpt-5.3-codex" = "gpt-5.4"
You can remove the default models specified in the top of the config, and the outcome is the same. You can also remove the model_catalog_json as well, and still the model selector does not appear. I'll attach my model catalog_json as well.
What is the expected behavior?
I expect that I'll see the model selector in the GUI and that I'm able to switch between available models, and various reasoning efforts per given thread and conversation, exactly how I can do so in the CLI.
Additional information
_No response_
7 Comments
<img width="800" height="217" alt="Image" src="https://github.com/user-attachments/assets/5edff2ec-66b7-4ba3-b8d0-ecd50b002d12" />
Here's how the GUI looks (note no model selector is available)
Potential duplicates detected. Please review them and close your issue if it is a duplicate.
Powered by Codex Action
This applies to the CLI too.
also reproducible with Moon Bridge + DeepSeek
The most useful clue here is the asymmetry you already found: the Automations tab shows the picker but the main thread does not, in the same app build, with the same catalog. That rules out your catalog file and your provider auth as the cause. Both surfaces can see the models, so something gates the main-thread picker specifically.
Two things to check to narrow it down:
available_modelsarray and adefault_model; see #14008 for the same config blob). For a custommodel_provider, that allowlist comes back empty, so the picker has nothing to render and hides itself. The Automations tab reads the catalog directly, which is why it still works. You can confirm by opening the app's Local Storage in DevTools and looking for that config.model = "oca/gpt-5.4"is provider-prefixed. The CLI resolvesprovider/modelfine, but the app's main-thread selector may match that string against the (empty) server list and fall back to nothing instead of to your catalog. Try a baremodel = "gpt-5.4"withmodel_provider = "oca"and see if the picker behaves differently, even if it doesn't fix the disappearance.So this points at the app filtering the conversation model list through an OpenAI-account-scoped source that custom providers never populate, rather than a config mistake on your end.
This is a recurring gap in BYOK desktop setups where the GUI assumes the first-party model list. We ran into the same thing on PawWork (an opencode fork, https://github.com/Astro-Han/pawwork) and ended up driving the in-conversation model/provider switcher straight off the provider config and catalog, with no separate server allowlist, so a custom provider's models land in the picker the same way the first-party ones do. Different codebase, but the fix is the same shape: the conversation picker should read from the same source the CLI and the Automations tab already use.
Huge +1
Same