Codex Desktop resolves namespaced current model against wrong list, shows "Custom" and gpt-5.3-codex
What version of the Codex App are you using (From “About Codex” dialog)?
Codex Desktop 26.803.5235.0 (bundled codex-cli 0.147.0-alpha.6.5)
What subscription do you have?
Company-managed custom OpenAI-compatible provider; no direct ChatGPT subscription is used for this reproduction.
What platform is your computer?
Microsoft Windows 11 Pro 10.0.26200 (Build 26200.8875), x64
What issue are you seeing?
When Codex is configured with a custom model_provider and model_catalog_json, the Desktop model picker renders the custom catalog correctly but resolves the currently selected model against a different list than the one it displays.
The lookup misses, so:
- The model picker chip displays "Custom" instead of the catalog entry's
display_name. /modelreportsgpt-5.3-codexas the current/default model, even though that model exists in neither the custom catalog nor the user configuration.- The reasoning-effort selector and model list render correctly from the custom catalog.
- Routing still works: turns run on the configured model and the thread records that provider-prefixed model correctly.
Sanitized configuration shape:
# managed_config.toml
model_provider = "custom_provider"
model_catalog_json = "<path-to-custom-catalog>"
# config.toml
model = "custom_provider/gpt-5.6-sol"
The provider identifier, endpoint, credentials, and nonessential catalog identifiers are intentionally omitted or anonymized.
Potential root cause from the bundled app-initial-*.js
The current-model resolver matches the .model field using exact string equality:
function She(models, cfg) {
return cfg == null ? null : models.find(m => m.model === cfg) ?? null;
}
function The({ automation, models }) {
let n = She(models, automation.model);
if (n != null) {
return { model: n.model, reasoningEffort: Che({ model: n, ... }) };
}
// ...
let r = whe(models);
return r == null
? { model: automation.model ?? "gpt-5.3-codex", reasoningEffort: ... }
: { model: r.model, reasoningEffort: ... };
}
At picker-resolution time, the models passed to The() appears to be the app's built-in cache, whose .model values are bare OpenAI slugs such as gpt-5.6-sol and gpt-5.3-codex. The model/list result rendered by the picker instead contains provider-prefixed values such as custom_provider/gpt-5.6-sol.
Therefore:
She(builtinList, "custom_provider/gpt-5.6-sol")
returns null, after which the label falls back to the localized composer.mode.local.model.custom value and the current model falls back to the hardcoded gpt-5.3-codex.
The routing layer resolves the same configured model against the custom catalog successfully, which explains why routing works while the current-model UI does not.
What steps can reproduce the bug?
- Configure a custom
model_providerwith amodel_catalog_jsonwhose model IDs carry a provider prefix, such ascustom_provider/.... - Set
config.tomlmodelto one of those provider-prefixed IDs. - Restart Codex Desktop so the managed configuration and catalog are loaded.
- Open the Desktop model picker.
- Observe that the model list and reasoning-effort choices render from the custom catalog, but the current-model chip displays "Custom".
- Run
/modeland observe that it reportsgpt-5.3-codex. - Start a turn and inspect the thread record or gateway routing; the configured provider-prefixed model is used correctly.
What is the expected behavior?
The picker should resolve the current model against the same model/list result it renders. A configured model that is present in that list should display its catalog name and supported reasoning options instead of falling back to "Custom" or an unrelated hardcoded model.
The preferred fix is to use the effective model/list result for current-model resolution. If that is not possible, namespace normalization could be a fallback before exact comparison, while preserving the custom catalog's metadata as authoritative.
Additional information
Evidence that display and resolution use different sources:
model/listovercodex app-serverreturns the provider-prefixed model IDs that the picker renders.- A live turn records the same provider-prefixed model on the thread, confirming that routing resolved it.
- The picker chip still displays "Custom" and
/modelreportsgpt-5.3-codex.
Related but distinct reports:
- #19694 and #34487 concern custom catalog models being hidden or omitted. In this reproduction, the catalog and model list render correctly.
- #36582 reports a related custom-provider label problem plus incorrect session persona. In this reproduction, routing and recorded thread model are correct; the isolated failure is current-selection resolution, with the exact hardcoded
gpt-5.3-codexfallback identified. - Merged PR #12658 added namespaced-model matching in the open-source model metadata path (
oai/gpt-5.3-codex→gpt-5.3-codex). That is useful precedent for handling provider-prefixed IDs, although the Desktop UI should preferably resolve against its effective custom catalog rather than substitute built-in metadata.
1 Comment
Additional evidence (Windows, Codex Desktop) supporting the "Custom" /
gpt-5.3-codexfallback for namespaced custom models.Versions
codex-cli 0.147.0, Desktop MSIX26.803.10989.0(internal26.803.81509), auth modeApiKey, custommodel_provider = "litellm".Verbatim resolver fragment from the app renderer bundle (
webview/assets/app-initial-KpqQCW_k.js) — when a model has nodisplayNamein the app's known model list, the current-model label falls back to theCustomlocalization string:Full current-model resolver (label fallback to "Custom"):
Also present in the same bundle — the hardcoded default-model fallback when no model can be resolved:
Observed: a namespaced custom model such as
neuralwatt/deepseek-v4-flashhas nodisplayNamein the app's model list, so the resolver falls back toCustom(and togpt-5.3-codexwhen resolution fails) — matching the reported behavior. The CLI (codex debug models) lists all 14 custom slugs correctly withvisibility=list.