Codex Desktop ignores project-local model_catalog_json for fresh project threads
This bug report was written by Codex.
Summary
Codex Desktop appears to ignore a trusted project's .codex/config.toml model_catalog_json setting when creating a fresh project thread, while the CLI/debug path from the same cwd respects it.
This prevents project-local custom model catalogs from working in Desktop, including per-model base_instructions / model_messages.instructions_template changes.
Environment
- OS: Windows
- Codex Desktop fresh-thread originator in rollout:
Codex Desktop - Desktop rollout
cli_version:0.136.0-alpha.2 - Desktop rollout source:
vscode - CLI test originator in rollout:
codex-tui - CLI test
cli_version:0.130.0-alpha.5 - Project cwd:
F:\Projects\_sandbox
Project Config
Project-local config was created at:
# F:\Projects\_sandbox\.codex\config.toml
model_catalog_json = "model-catalog.json"
The referenced catalog exists at:
F:\Projects\_sandbox\.codex\model-catalog.json
The catalog was generated from the normal Codex model cache, but with the trailing ## Intermediary updates section removed from both fields for every model entry:
base_instructionsmodel_messages.instructions_template
This was done for all cached models:
gpt-5.5gpt-5.4gpt-5.4-minigpt-5.3-codex-sparkcodex-auto-review
Expected Behavior
When creating a fresh Codex Desktop thread in trusted project F:\Projects\_sandbox, Desktop should load the project-local .codex/config.toml, resolve model_catalog_json = "model-catalog.json" relative to the project .codex directory, and use the corresponding catalog entry for the selected model.
In this test, a fresh Desktop thread using gpt-5.5 should have received base instructions without ## Intermediary updates.
Actual Behavior
Fresh Codex Desktop project threads continued to receive base instructions containing ## Intermediary updates, despite the project-local catalog having that section removed from both base_instructions and model_messages.instructions_template.
Two Desktop test threads showed the issue:
codex://threads/019e90cd-abd3-7f73-99fe-2f9015a40047
codex://threads/019e90d5-47f0-7e81-861f-abe3778312d8
Inspection of the persisted rollout for 019e90d5-47f0-7e81-861f-abe3778312d8 showed:
Cwd: F:\Projects\_sandbox
Originator: Codex Desktop
Source: vscode
cli_version: 0.136.0-alpha.2
SessionHasUpdates: True
ProjectBaseHasUpdates: False
ProjectTemplateHasUpdates: False
GlobalBaseHasUpdates: True
GlobalTemplateHasUpdates: True
The Desktop session base instructions still contained the stripped section, indicating Desktop did not use the project-local catalog.
CLI Control Test
Running the CLI from the same cwd did respect the project-local catalog.
CLI test thread:
codex://threads/019e90dd-884f-7b01-b969-988e3e9f1a69
Inspection of that rollout showed:
Cwd: F:\Projects\_sandbox
Originator: codex-tui
Source: cli
cli_version: 0.130.0-alpha.5
SessionHasUpdates: False
SessionHasUpdateAsYouGo: False
ProjectBaseHasUpdates: False
ProjectTemplateHasUpdates: False
GlobalBaseHasUpdates: True
GlobalTemplateHasUpdates: True
codex debug models from F:\Projects\_sandbox also loaded the project-local catalog and reported that both base_instructions and model_messages.instructions_template no longer contained ## Intermediary updates.
Why This Matters
The documented project-local .codex/config.toml config path is useful for repo-specific model catalog overrides, including per-model base-instruction tuning. It currently appears to work in CLI/debug but not in Desktop project thread creation.
This makes it difficult to use project-local model catalog behavior in Desktop without changing global C:\Users\<user>\.codex\config.toml, which is too broad for repo-specific experiments.
Notes
One remaining initial progress note in the CLI test was expected because a separate injected AGENTS wrapper rule said:
For multi-step or tool-heavy work, start with a short progress note stating the first step.
That is separate from the model catalog behavior. The important distinction is that the CLI session did not receive the stripped ## Intermediary updates model-base section, while Desktop did.
3 Comments
Potential duplicates detected. Please review them and close your issue if it is a duplicate.
Powered by Codex Action
Hi, just wanted to bump this — is there any update on the priority here?
This has been open since June 2026 and it's a real blocker for anyone using project-scoped model_catalog_json with the Desktop app (as opposed to CLI, where it works fine from the same cwd). It also compounds with #19694 (Desktop picker filtering out locally configured models), so between the two, third-party/custom model configs are effectively unusable in Desktop project threads right now.
Is this something that's queued for a fix, or is project-local model_catalog_json support in Desktop considered out of scope going forward? Would help to know so we can plan around it (e.g. moving everything to the global config as a workaround) rather than waiting on something that isn't planned.
I reproduced this on Codex 0.147.0 with the Windows Codex App connected to a shared Linux app-server, and traced the project-local catalog path on current main (
95aada11c).The project config is loaded, and
Config::to_models_manager_config()does passmodel_catalog: self.model_catalog.clone(). The loss happens later:ThreadManagerowns one sharedModelsManager, constructed from the app-server startup config bybuild_models_manager().ModelsManagerConfigcontaining its project catalog toget_model_info().construct_model_info_from_candidates(model, candidates, config)selects metadata only from the sharedcandidates(remote_models) and then applies scalar overrides.config.model_catalog.So the project catalog is parsed, but cannot participate in per-thread model resolution. This explains the CLI/Desktop split: a CLI launched inside the project constructs its manager from that project's startup config; the shared Desktop app-server was created before the project cwd/config existed.
A minimal regression test would:
get_model_info()withModelsManagerConfig.model_catalog = Some(catalog B)for the same slug;The current workaround is to put
model_catalog_jsonin the app-server startup/user config and restart it, but that broadens a project-scoped override globally.This also compounds the custom-instruction/Responses Lite regression reported in #38355: disabling Lite via a project catalog would be a temporary mitigation, but this issue prevents Desktop from applying it.