Desktop custom providers are unusable with existing chats and the model picker
Related to #10867, but this is a more specific Desktop failure mode around custom providers, the model catalog, and existing chat history.
Summary
Codex CLI/TUI can use custom model providers reasonably well via model_providers and profiles. Codex Desktop cannot do the same in a sane, safe way.
The current Desktop behavior makes third-party providers such as OpenRouter/LiteLLM/internal OpenAI-compatible gateways effectively unusable if the user wants all three of these at once:
- use the Desktop GUI
- select custom-provider models from the GUI model picker
- keep access to existing Desktop chats/history
I spent time trying to make Codex Desktop use OpenRouter models while preserving normal chats. Every available path either fails outright, hides/segregates chats, replaces the built-in catalog, or requires temporary mutation of ~/.codex/config.toml.
Environment
- macOS arm64
- Codex CLI/App Server:
0.142.0-alpha.1 - Codex Desktop:
26.616.32156 - Provider tested: OpenRouter, Responses API-compatible
- Example models tested:
moonshotai/kimi-k2.7-codedeepseek/deepseek-v4-proqwen/qwen3-coder-plusz-ai/glm-5.2minimax/minimax-m3
What works
CLI/TUI usage works with custom provider config, for example:
[model_providers.openrouter]
name = "OpenRouter"
base_url = "https://openrouter.ai/api/v1"
env_key = "OPENROUTER_API_KEY"
wire_api = "responses"
supports_websockets = false
[profiles.deepseek]
model_provider = "openrouter"
model = "deepseek/deepseek-v4-pro"
model_reasoning_effort = "high"
Then:
codex --profile deepseek
That path is understandable and relatively safe.
What fails in Desktop
1. Desktop has no supported provider-aware model picker
Generated app-server protocol/schema inspection shows model/list only accepts:
cursorlimitincludeHidden
The Model response does not include provider identity. The Desktop frontend calls list-models-for-host with includeHidden: true, but there is no provider-aware external model list surfaced to the picker.
Desktop model changing appears to update model/reasoning, not provider.
2. Provider support exists at thread creation, but not as a normal GUI switch
The app-server protocol has ThreadStartParams.modelProvider, which suggests provider-aware threads are supported at creation time.
However, TurnStartParams can override model but not modelProvider, so the GUI has no obvious way to switch provider per turn/thread from the existing composer flow.
This makes custom-provider usage feel half-wired: the backend/config understand providers, but the Desktop UX does not expose a coherent model/provider switcher.
3. model_catalog_json is replacement-only and dangerous for Desktop UX
model_catalog_json looks like it should solve this, but it replaces the bundled catalog for the current process. It does not append or merge custom models into the normal catalog.
Public source comments also describe it as a replacement mechanism. In practice, using it to add custom OpenRouter models removes the normal Codex/OpenAI model catalog from that process.
This is dangerous because it looks like an extension point but behaves like a full override. A user trying to add one provider/model can accidentally make the normal Desktop model picker/catalog behavior disappear.
4. Saved custom models fall back if absent from the active catalog
Desktop model settings code falls back when the saved model is not present in list-models data. So simply putting a custom model ID in config is not enough to make Desktop behave like that custom model is a normal selectable GUI model.
5. Separate CODEX_HOME avoids damaging the main config but loses normal chats
Launching Desktop with a separate CODEX_HOME can load a custom provider/catalog safely, but then the GUI does not show the user’s normal chats because chats/state are tied to the active Codex home and Electron app state.
Trying to symlink the main state/session files into the alternate home is not reliable enough: the GUI can show folders/projects but still not show the actual chats as expected. That implies there is more Desktop UI state/provider filtering involved than just the raw state DB files.
6. Using the real ~/.codex profile requires unsafe config mutation
The only path that seems likely to preserve all chats is launching the real Desktop profile (~/.codex) after temporarily editing ~/.codex/config.toml to set:
model_provider = "openrouter"model = "..."model_catalog_json = "..."[model_providers.openrouter] ...
Then restoring the config after Desktop quits.
That is not a sane supported workflow. It is fragile, risks leaving the main config in a broken state if the launcher crashes, and is exactly the kind of workaround that can make users think their chats/models disappeared.
7. Fake-auth workarounds are not a real solution
The requires_openai_auth = true workaround mentioned in #10867 can make parts of the picker render, but it is conceptually wrong: it requires fake OpenAI auth and still does not provide a proper multi-provider/multi-profile Desktop switcher.
It also does not solve the underlying issue that Desktop treats OpenAI/ChatGPT auth/catalog state as the gate for model switching rather than treating configured providers as first-class model sources.
Expected behavior
Codex Desktop should support custom providers as first-class GUI targets:
- The model picker should be provider-aware.
- Custom provider models should be discoverable from
/v1/modelswhere possible, or from explicit configured model IDs. - There should be a safe append/merge mechanism for custom model catalog entries.
model_catalog_jsonshould either be renamed/documented as full replacement, or a separate additiveextra_model_catalog_json/model_catalogsmechanism should exist.- Switching provider/model should not make existing chats appear gone.
- If threads are intentionally provider-scoped, the UI should make that explicit and provide an “all providers/all chats” view.
- Users should not need fake OpenAI auth, separate
CODEX_HOME, symlinks, or temporary config rewrites.
Why this feels intentional/adversarial from the user side
I am not claiming to know maintainer intent. But the current behavior creates a reasonable perception of intentional lock-in or bad faith because the failure is not a single missing UI checkbox.
Several independent pieces all push in the same direction:
- Custom providers work in CLI config, proving the core abstraction exists.
- Desktop does not expose those providers as first-class picker options.
- The app-server protocol supports
modelProviderat thread start, but the GUI does not provide a normal provider switcher. model_catalog_jsonlooks like a customization hook, but actually replaces the full bundled catalog.- Changing provider/profile can make existing chats appear missing or segregated.
- Workarounds require fake OpenAI auth, alternate homes, symlinks, restarts, or risky config mutation.
- The product does not clearly warn users that these paths can hide normal Desktop model/chat behavior.
From a user perspective, this is indistinguishable from a deliberate product boundary: custom providers are supported just enough for CLI/power users, but Desktop remains effectively tied to the built-in OpenAI/ChatGPT catalog and auth flow.
If this is not intentional, the Desktop app needs a clear supported path for custom providers. If it is intentional, the limitation should be documented plainly instead of leaving users to discover it by breaking their Desktop model picker or thinking their chats disappeared.
Request
Please clarify the intended support level for custom model providers in Codex Desktop, and ideally implement first-class Desktop support for:
- provider selection
- custom-provider model discovery
- additive custom model catalog entries
- preserving/seeing existing chats across provider changes
At minimum, please document that model_catalog_json is a full catalog replacement and not a safe way to add models to Desktop.
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗