Skip to content

Desktop custom providers are unusable with existing chats and the model picker #29156

Open

Description

@nostitos

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-code
    • deepseek/deepseek-v4-pro
    • qwen/qwen3-coder-plus
    • z-ai/glm-5.2
    • minimax/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:

  • cursor
  • limit
  • includeHidden

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/models where possible, or from explicit configured model IDs.
  • There should be a safe append/merge mechanism for custom model catalog entries.
  • model_catalog_json should either be renamed/documented as full replacement, or a separate additive extra_model_catalog_json/model_catalogs mechanism 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 modelProvider at thread start, but the GUI does not provide a normal provider switcher.
  • model_catalog_json looks 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.

Activity

  1. added
    enhancementNew feature or request
    appIssues related to the Codex desktop app
    custom-modelIssues related to custom model providers (including local models)
    app-serverIssues involving app server protocol or interfaces
    configIssues involving config.toml, config keys, config merging, or config updates
    sessionIssues involving session (thread) management, resuming, forking, naming, archiving
    on Jun 20, 2026
  2. bradleygibsongit commented on Jun 21, 2026

    @bradleygibsongit

    +1

  3. clavelinaswykoniki-cell commented on Jun 24, 2026

    @clavelinaswykoniki-cell

    Adding another user-impact case for this issue, focused on Desktop custom-provider support for common China-region model providers.

    Environment:

    • Product: Codex Desktop on macOS
    • macOS: 26.5.1, arm64
    • Codex Desktop: 26.616.81150, build 4306
    • Bundled CLI/app-server: codex-cli 0.142.0

    User expectation:

    With the recent "native" custom-provider support, users expect Codex Desktop to make it possible to add and use common third-party providers from the GUI, including providers/models such as DeepSeek, Moonshot/Kimi, Qwen/DashScope, Zhipu/GLM, MiniMax, Doubao/Volcengine, Baidu/ERNIE, Tencent/Hunyuan, or compatible gateways such as OpenRouter/LiteLLM.

    Observed product problem:

    • Desktop still does not feel provider-aware in the model picker.
    • It is unclear which providers/models are expected to work natively, which require a Responses API proxy, and which are unsupported because they only expose Chat Completions style APIs or provider-specific request fields.
    • Users can see that some custom/third-party models work through CLI profiles or adapters, but the Desktop GUI path is not clear or first-class.
    • For China-region providers specifically, the practical outcome is that only a subset can be used directly, while many require custom proxying or are not usable from Desktop in a normal way.

    I am not claiming all providers listed above have been fully reproduced in this exact environment; this is a user-impact report and compatibility/documentation request. The current behavior makes Desktop custom-provider support hard to evaluate and easy to misconfigure.

    Suggested improvements:

    • Make the Desktop model picker provider-aware.
    • Add an explicit compatibility matrix or docs for custom providers, including whether Codex requires /v1/responses semantics vs /chat/completions.
    • Provide an additive way to register custom provider models without replacing or hiding the built-in model catalog.
    • Surface provider-specific failure reasons in the UI instead of leaving the user with a generic non-working model selection.
    • Preserve normal chat visibility when switching provider/model configurations.

    This matches the broader issue here: custom providers appear partly supported by config and app-server primitives, but Desktop does not expose a coherent, safe path for everyday use.

  4. brotherhc commented on Jul 29, 2026

    @brotherhc

    This remains a significant product limitation in the current macOS desktop app (26.721.41059, build 5848).

    The official documentation says the desktop app, CLI, and IDE extension share ~/.codex/config.toml, and the CLI supports custom model providers. In practice, however, Desktop still does not offer a provider-aware model picker or a reasonable way to switch between configured third-party models.

    For an ordinary Desktop user, “support” currently means manually editing global configuration, managing credentials outside the app, restarting the app, and starting a new task. That is not credible first-class support, especially when the CLI already has profiles and explicit provider/model controls.

    The UX is particularly frustrating because:

    • the model picker presents only the built-in ChatGPT/OpenAI catalog;
    • configured providers and model IDs are not exposed as selectable options;
    • there is no GUI for provider credentials, compatibility status, or connection testing;
    • switching models safely requires configuration edits and restarts;
    • the product documentation describes shared provider support without clearly stating that Desktop lacks a usable selection workflow.

    Please either make configured providers first-class entries in the Desktop model picker, with provider/model selection and connection validation, or document the Desktop limitation plainly. The current half-supported state is confusing and feels unnecessarily restrictive.

    A new Desktop build (26.721.81911, build 5973) was offered on July 29, but its appcast contains no release notes, so users also cannot determine whether this limitation has changed without installing and retesting it.

  5. hasani114 commented on Aug 1, 2026

    @hasani114

    We need this feature asap. Its annoying to have to go through so many hoops to use other models in the Desktop App,

  6. nostitos commented on Aug 2, 2026

    @nostitos
    Author
  7. rimanov2000 commented on Aug 5, 2026

    @rimanov2000

    Reproduced on Windows 11, Codex Desktop 26.730.8199.

    With a ChatGPT-authenticated parent task on GPT-5.6 Terra and a valid custom DeepSeek provider configuration, selecting deepseek-v4-flash in the Desktop model picker reports “Model changed…” and then fails:

    The 'deepseek-v4-flash' model is not supported when using Codex with a ChatGPT account.

    The custom provider itself is functional: a named custom subagent with model_provider = "deepseek" and model = "deepseek-v4-flash" successfully ran while the GPT parent remained untouched. This appears to be a Desktop-picker routing bug: it changes the model name without atomically selecting the configured provider.

    Please make the picker provider-aware, or hide incompatible custom-provider model IDs until the provider can be selected.

  8. rimanov2000 commented on Aug 5, 2026

    @rimanov2000

    sorry for slop, but regardless what i've tried it did not work.

  9. claell commented on Aug 23, 2026

    @claell

    Supporting this request. The combination of a non-provider-aware model picker, provider-bucket session filtering (#24648), and full-catalog replacement semantics for model_catalog_json makes custom providers hard to use safely on Desktop today.

    Even a documented support matrix (which surfaces are custom-provider aware, and whether model_catalog_json replaces or merges) would help third-party tools set correct expectations.

  10. Miensoap commented on Aug 29, 2026

    @Miensoap

    Root cause: the picker swaps the model slug but never rebinds the thread's persisted provider

    Reproduced and root-caused this on Codex App 26.825.32147 (bundled codex-cli 0.150.0-alpha.12.2; same behavior driving codex app-server from codex-cli 0.150.1), macOS arm64, signed in with a ChatGPT account, custom Responses-compatible provider configured through model_providers + model_catalog_json.

    Measured failure. On an existing thread whose persisted binding is the built-in OpenAI provider, selecting a custom-provider model in the thread's model picker and sending a message fails the turn with the raw backend response recorded in thread_turns.error_json:

    {"type":"error","status":400,"error":{"type":"invalid_request_error",
     "message":"The '<custom-model>' model is not supported when using Codex with a ChatGPT account."}}

    The request never reaches the custom provider - it is sent to the ChatGPT-account backend because the thread is still bound to the built-in OpenAI provider.

    Where the binding lives. The per-thread provider is persisted in the engine thread registry (~/.codex/state_*.sqlite, table threads: model, model_provider, reasoning_effort) and is what thread/resume returns as model/modelProvider (verified with a plain thread/resume through codex app-server: the response follows the registry row immediately, in both directions). New threads pick up the config-default provider; resumed threads keep their persisted one.

    Diagnosis. The model picker updates only the model selection for the thread; it does not rebind the persisted provider. So a custom-provider model selected on an OpenAI-bound thread is routed to the ChatGPT backend and rejected. Out of spec, but to confirm the routing side: updating that registry row to the custom provider makes subsequent resumes/turns use the custom provider correctly - the routing information exists, the picker just never writes it.

    Suggested directions.

    • when the picker selects a model that belongs to a custom provider (from model_catalog_json / model_providers), rebind the thread's provider together with the model; or
    • surface provider rebinding explicitly in the picker UI (e.g., a per-thread provider selector); or
    • at minimum, warn instead of offering models the thread's current provider cannot serve.

    Happy to provide full session ids / exact configs if useful.

  11. Razkar1 commented on Sep 3, 2026

    @Razkar1

    Confirmed on the current macOS Desktop build 26.901.20858 (build 7658) with a custom Responses-compatible provider.

    I was able to get a custom model and the normal ChatGPT/Codex models to coexist in the native Desktop model selector. The remaining failure was provider binding: selecting the custom model for a new thread still produced:

    The '<custom-model>' model is not supported when using Codex with a ChatGPT account.
    

    The useful finding is that the backend path already supports the desired behavior. On build 7658 I patched only the Desktop request layer so that, when a new thread starts, the selected model is accompanied by the correct ThreadStartParams.modelProvider. I also applied the same routing to the prewarmed-thread path. No global model_provider was set, and the normal ChatGPT login remained active.

    With that one change, the behavior became:

    built-in Codex model -> modelProvider = openai -> signed-in ChatGPT account
    custom model         -> modelProvider = custom provider -> configured Responses endpoint
    

    This worked live: muse-spark-1.3-contributor was routed through a local meta_model_api provider while GPT-5.6 Sol/Terra/Luna and GPT-5.3 Codex Spark continued using the signed-in ChatGPT/OpenAI provider. The custom and built-in models remained visible in the same native selector.

    That strongly suggests the missing Desktop feature for new threads is relatively narrow: the picker/model-selection layer needs to preserve or resolve provider identity and send modelProvider together with model on thread/start (including the prewarm path). The app-server already accepts it and routes correctly.

    This does not solve mid-thread provider switching; as noted elsewhere in this issue, existing threads have a persisted provider binding and would need explicit rebinding or a new thread.

    I published the build-7658 proof-of-concept here:

    Keksuccino/Better-Codex-App-Custom-Provider-Support#3

    The patch intentionally leaves the native model picker unchanged and only injects per-model provider routing at thread creation. It also fails closed on source-layout changes. I hope this helps narrow a native implementation: provider-aware model entries + modelProvider on thread creation appear sufficient for the new-thread case.

  12. waeljammal commented on Sep 16, 2026

    @waeljammal

    Adding my 2 cents here as well, we really need this too. We would really like to be able to switch between our private models and gpt models.

  13. sudeekshamurari commented on Sep 23, 2026

    @sudeekshamurari

    Additional reproduction and app-server interface detail for the existing-thread case. Provider/model identifiers below are placeholders.

    Reproduction

    Environment: Codex Desktop on macOS, signed in with a ChatGPT account, with a custom Responses-compatible provider configured through model_providers and models exposed through model_catalog_json.

    1. Open an existing task created with the built-in provider.
    2. Select a custom-provider model in the Desktop model picker.
    3. Send a short prompt.

    The turn fails with:

    The '<custom-model>' model is not supported when using Codex with a ChatGPT account.
    

    The selected model is not routed to the custom endpoint. A fresh task can work when the custom provider is resolved at thread creation, so this is not a blanket failure of the custom Responses endpoint.

    Interface gap

    In the app-server source/schema inspected:

    • Model entries returned by model/list have model IDs and reasoning capabilities, but no provider identity (codex-rs/app-server-protocol/src/protocol/v2/model.rs).
    • ThreadSettingsUpdateParams accepts model and effort, but no modelProvider. In contrast, the resulting ThreadSettings includes model_provider (codex-rs/app-server-protocol/src/protocol/v2/thread.rs).
    • merge_persisted_resume_metadata restores the persisted model/provider when there is no explicit model/provider resume override (codex-rs/app-server/src/request_processors/thread_processor.rs).

    This is consistent with the earlier reports: changing a model slug through the picker/settings path does not provide an atomic way to change the thread's provider binding.

    Expected behavior

    Selecting a configured model should resolve and apply its provider together with the model, preserving the current conversation. The same operation should validate the reasoning effort against the selected model's supported values and use its default if the previous value is incompatible.

    A possible implementation is provider identity in model entries plus provider rebinding in thread/settings/update. New-thread and prewarm paths should likewise carry the selected provider with the model. Users should be able to use the normal picker without editing session databases, restarting the app, or abandoning an existing task.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    appIssues related to the Codex desktop appapp-serverIssues involving app server protocol or interfacesconfigIssues involving config.toml, config keys, config merging, or config updatescustom-modelIssues related to custom model providers (including local models)enhancementNew feature or requestsessionIssues involving session (thread) management, resuming, forking, naming, archiving

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions