Support custom model providers in app

Open 💬 20 comments Opened Feb 6, 2026 by iwinux

What version of the Codex App are you using (From “About Codex” dialog)?

260205.1301 (554)

What subscription do you have?

None

What issue are you seeing?

I'm using custom model provider for Codex CLI and App. In CLI, /model is available and switching models works as expected. However, in Codex App, this happens (notice the absence of model switcher):

<img width="2558" height="336" alt="Image" src="https://github.com/user-attachments/assets/c6181aaf-7681-4049-91ad-dd79d81f2dd2" />

What steps can reproduce the bug?

  1. set up custom model provider & profile

```toml
profile = "internal"

[features]
# remote_models = false

[profiles.internal]
# model = "gpt-5.2"
model_provider = "internal"
model_reasoning_effort = "medium"

[model_providers.internal]
name = "Interal LLM Gateway"
base_url = "http://llms.example.com/v1"
wire_api = "responses"
```

  1. launch Codex App

What is the expected behavior?

It should provider the same model switcher as in ChatGPT subscription mode:

<img width="2570" height="352" alt="Image" src="https://github.com/user-attachments/assets/291d0409-ab49-4eec-8f7b-0317ffc648f2" />

Additional information

_No response_

View original on GitHub ↗

20 Comments

harshit97 · 4 months ago

Ran into the same thing

I'm using a custom model provider behind an internal gateway and hit exactly this: CLI works great (/model lets me switch), but the Desktop app (v26.217.1959, build 669, as of Feb 19 2026) just... doesn't show the model picker at all.

Spent some time tracing through the Electron bundle to figure out what's going on. For anyone unfamiliar, the Codex Desktop app is an Electron app, and you can extract its bundled source from the asar archive at Codex.app/Contents/Resources/app.asar using npx asar extract.

Root cause: In the frontend JS (index-X7Ur8m0p.js inside the extracted asar), the composer footer component conditionally renders both the model picker and the reasoning effort picker only when requiresAuth is truthy:

const c = o.requiresAuth;
c && f.jsxs(f.Fragment, {children: [
    !S && f.jsx(Lpn, {conversationId: e}),  // model picker
    !M && f.jsx(Fpn, {conversationId: e})    // reasoning effort picker
]})

When you use a custom model provider without a ChatGPT or OpenAI API key login, the frontend sees requiresAuth = false and the entire picker block gets skipped. The backend side is actually fine — models pass filtering correctly and the frontend allowlist (mft) includes models like gpt-5.2-codex and gpt-5.2 for non-ChatGPT auth. It's purely a UI gating issue.

Also worth noting: the source on main as of Feb 21 2026 has a model_catalog_json config key (in codex-rs/config/src/lib.rs) that looks like it was meant to help with this — letting you point to a local model catalog file. But it doesn't exist in the current shipped binary (v26.217.1959, build 669). Setting it in config.toml is silently ignored.

What I think the fix would look like:

The conditional should render the model picker regardless of auth mode when there are models available. Something like showing the picker when requiresAuth || hasCustomProvider — the model filtering logic already handles scoping the list correctly per auth type.

Would love to see this fixed since the Desktop app is otherwise really nice with custom providers.

adrixgc · 4 months ago

Ran into the same/similar problem

We are using LiteLLM as a proxy to expose custom models via /v1/models. The endpoint correctly returns our custom model IDs, for example:

{
  "data": [
    {
      "id": "desarrollo-v4-gpt-5.3-codex",
      "object": "model",
      "created": 1677610602,
      "owned_by": "openai"
    },
    {
      "id": "desarrollo-v4-gpt-5.2-codex",
      "object": "model",
      "created": 1677610602,
      "owned_by": "openai"
    },
    {
      "id": "desarrollo-v4-gpt-5.1-codex-mini",
      "object": "model",
      "created": 1677610602,
      "owned_by": "openai"
    }
  ],
  "object": "list"
}

Problem:

The issue is not with LiteLLM or the endpoint itself.

The problem is that Codex CLI and Codex Desktop UI do not properly support or expose these custom models:

  • Users cannot easily discover available models
  • They must manually input exact model IDs in config.toml
  • The CLI/UI do not present these models as selectable options

Expected Behavior:

  • Codex CLI and Desktop UI should:
  • Fetch models from gateway's /v1/models
  • Display them as selectable options
  • Allow users to choose custom LiteLLM models without manual configuration

Why this matters:

Without this, using LiteLLM as a model gateway breaks the UX:

  • No model discoverability
  • High friction for users
  • Not viable for non-technical users
adrixgc · 4 months ago

Quick question @iwinux @harshit97

Have any of you managed to get the gateway's /v1/models endpoint to properly populate the model list in the Codex CLI?

In our case, the endpoint returns the custom models correctly, but the CLI doesn’t seem to expose them as selectable options (we still need to manually set the model ID).

Just trying to understand if this is a general limitation or if someone has found a workaround.

Thanks!

EvanNotFound · 4 months ago

Just add

requires_openai_auth = true

under your provider.

then, restart the app. if the app asks you to login, choose api key, and enter any character then press enter

this is a workaround

tranleo148 · 3 months ago
Just add `` requires_openai_auth = true `` under your provider. then, restart the app. if the app asks you to login, choose api key, and enter any character then press enter this is a workaround

it worked for me. thanks

iwinux · 3 months ago

Latest version on macOS (Version 26.406.31014 (1395)) seems to have fixed the issue. Is there any changelog entry confirming this?

fjfricke · 3 months ago

It only allows one profile though...

chrismartinez-sb · 2 months ago
Ran into the same thing I'm using a custom model provider behind an internal gateway and hit exactly this: CLI works great (/model lets me switch), but the Desktop app (v26.217.1959, build 669, as of Feb 19 2026) just... doesn't show the model picker at all. Spent some time tracing through the Electron bundle to figure out what's going on. For anyone unfamiliar, the Codex Desktop app is an Electron app, and you can extract its bundled source from the asar archive at Codex.app/Contents/Resources/app.asar using npx asar extract. Root cause: In the frontend JS (index-X7Ur8m0p.js inside the extracted asar), the composer footer component conditionally renders both the model picker and the reasoning effort picker only when requiresAuth is truthy: const c = o.requiresAuth; c && f.jsxs(f.Fragment, {children: [ !S && f.jsx(Lpn, {conversationId: e}), // model picker !M && f.jsx(Fpn, {conversationId: e}) // reasoning effort picker ]}) When you use a custom model provider without a ChatGPT or OpenAI API key login, the frontend sees requiresAuth = false and the entire picker block gets skipped. The backend side is actually fine — models pass filtering correctly and the frontend allowlist (mft) includes models like gpt-5.2-codex and gpt-5.2 for non-ChatGPT auth. It's purely a UI gating issue. Also worth noting: the source on main as of Feb 21 2026 has a model_catalog_json config key (in codex-rs/config/src/lib.rs) that looks like it was meant to help with this — letting you point to a local model catalog file. But it doesn't exist in the current shipped binary (v26.217.1959, build 669). Setting it in config.toml is silently ignored. What I think the fix would look like: The conditional should render the model picker regardless of auth mode when there are models available. Something like showing the picker when requiresAuth || hasCustomProvider — the model filtering logic already handles scoping the list correctly per auth type. Would love to see this fixed since the Desktop app is otherwise really nice with custom providers.

Do you know if the model_catalog_json is working correctly? As of now I tried using it for a custom picker of models in our org, but it ignored all of them except the 5.3 codex slug, I assume because thats an existing openai model?

wukemon · 2 months ago
快速提问@iwinux @harshit97 你们当中有人成功让网关的 /v1/models 端点在 Codex CLI 中正确填充模型列表吗? 在我们的案例中,端点正确返回了自定义模型,但 CLI 似乎没有将它们公开为可选选项(我们仍然需要手动设置模型 ID)。 我只是想了解这是否是一个普遍存在的限制,或者是否有人找到了解决方法。 谢谢!

The same issue, configure a custom model, the custom model does not appear in the codex app, and it is also not possible to select the custom model (only official models are available)

janis-ax · 2 months ago

Does anyone have an example with a workaround?

bakaburg1 · 2 months ago

Any news on this?

d11196175 · 1 month ago

any news on this issue?

steveepreston · 1 month ago

+1
Please add a custom model selector
Codex is a good app and we dont want to migrate to other tools like Cline/Roo/Windsurf/Continue/etc

steveepreston · 1 month ago

Currently for changing model from .toml file, should restart VSCode/Extension!!!

bakaburg1 · 1 month ago

even more annoying is that threads are segregated by provider. So if I change provider and restart I lose all previous threads...

HerShin5 · 1 month ago

Adding another gateway-user data point here: the picker really shouldn’t be tied to the ChatGPT/OpenAI auth path.

A lot of teams are running Codex behind an OpenAI-compatible /v1 gateway for routing, fallback, usage limits, or centralized billing. In that setup the important thing is: if a custom provider has base_url + wire_api = "responses" and /v1/models returns usable IDs, the app should expose those models just like the CLI does.

For anyone testing external gateways, I’d treat this the same way as LiteLLM/OpenRouter-style providers: keep the model IDs exactly as the gateway returns them and point Codex at a Responses-compatible base URL. For example, https://kiroforge.cloud/v1 is OpenAI-compatible, so it falls into the same class of custom-provider UX issues being discussed here.

The workaround with requires_openai_auth = true is useful, but long-term it would be much nicer if the Desktop app rendered the model picker whenever a custom provider has a discoverable model list, regardless of auth mode.

steveepreston · 1 month ago

@bakaburg1 Exactly

StellarWarp · 10 days ago

Blocking ab.chatgpt.com and removing leveldb can be temporary solution for not changing your provider, as it relates to service policies obtained online.

On Windows you can add 127.0.0.1 ab.chatgpt.com to C:\Windows\System32\drivers\etc\hosts and delete ~\AppData\Local\Packages\OpenAI.Codex_<some hash>\LocalCache\Roaming\Codex\web\Codex\Default\Local Storage\leveldb

However this would cause some other issue e.g. missing Approve for me

Pretty sure that OpenAI has no plan to improve this, cause they want to ensure that their closed app can use their close models only.

zierocode · 4 days ago

I rechecked this against Codex CLI 0.144.5. The current source now has most of
the transport needed for a custom provider to own its remote model catalog, but
not yet the provider-scoped authoritative semantics a gateway needs.

Current 0.144.5 behavior:

  • ModelProviderInfo supports command-backed bearer auth through auth, and

command-auth providers are eligible for remote model refresh.

  • Codex requests {base_url}/models?client_version=... with the provider's auth,

expects the Codex { "models": [ModelInfo...] } schema, captures the response
ETag, and applies a five-second request deadline.

  • OnlineIfUncached uses models_cache.json with a five-minute TTL; an ETag

change can trigger an online refresh.

  • Remote catalogs replace bundled models only for ChatGPT account auth. For a

custom command-auth provider, remote entries are merged into the bundled
OpenAI catalog.

  • The cache is not keyed by provider identity. The source contains a TODO noting

that switching providers can reuse another provider's fresh cache entry.

  • model_catalog_json is global and startup-only. It is not a catalog contract

attached to one model_providers.<id> entry.

Pinned source evidence:

Could custom providers opt into an explicit provider-scoped mode, for example:

[model_providers.gateway]
name = "Internal gateway"
base_url = "https://gateway.example/v1"
wire_api = "responses"
model_catalog_mode = "authoritative_remote"

[model_providers.gateway.auth]
command = "gateway-token"
refresh_interval_ms = 300000

Suggested semantics for authoritative_remote:

  1. It applies only to the selected custom provider. A successful schema-valid

remote catalog replaces bundled models for that provider instead of merging
unrelated OpenAI presets.

  1. Cache identity includes the provider config key, normalized base URL, catalog

mode, and Codex client version. It never includes or persists bearer tokens.

  1. Startup with OnlineIfUncached may use only a fresh matching provider cache.

On a cache miss, run command auth and fetch within the existing deadline. A
failure must not fall back to another provider's cache or bundled catalog;
retain the explicit configured model and surface the catalog error instead.

  1. Persist the provider-scoped ETag with the snapshot. A same-ETag notification

renews that cache's TTL; a changed ETag forces an online refresh.

  1. Send If-None-Match during refresh. 304 retains the scoped snapshot and

renews its TTL; a valid 200 atomically replaces models and ETag.

  1. Reject an ambiguous combination with global model_catalog_json, or document

one deterministic precedence rule.

Useful acceptance tests:

  • two command-auth providers sharing one CODEX_HOME never reuse each other's

models or ETag;

  • a restart with a fresh matching cache does not invoke the auth command;
  • a stale/missing cache invokes the command once and refreshes the selected

provider;

  • 304 preserves the snapshot, while 200 replaces rather than merges it;
  • a network/auth/schema failure never exposes unrelated bundled models;
  • switching back to the first provider restores only its scoped snapshot.

This complements, but does not by itself close, #10867. Provider-scoped catalog
truth is the backend requirement; Desktop still needs to render and select the
models returned for a custom provider. If #10867 should remain focused on the
picker, this catalog-mode work could be tracked as a linked model-provider issue.

steveepreston · 3 days ago

@zierocode do you say that there is good news on this feature request?