model_catalog_json from main fails in Codex App 0.148.0-alpha.9 after catalog schema change

Open 💬 1 comment Opened Aug 17, 2026 by HexBen123

What issue are you seeing?

The current codex-rs/models-manager/models.json from the repository's main branch cannot be loaded through model_catalog_json by the Codex binary bundled with Codex App 26.810.52044 (build 6662).

The embedded binary reports:

codex-cli 0.148.0-alpha.9

Loading the current catalog fails before any model request is made:

Error: failed to parse model_catalog_json path `/dev/stdin` as JSON: missing field `supports_parallel_tool_calls` at line 116 column 5

This is a model-catalog schema compatibility issue, not a context-window issue.

What steps can reproduce the bug?

On macOS:

curl -fsSL https://raw.githubusercontent.com/openai/codex/main/codex-rs/models-manager/models.json \
  | /Applications/ChatGPT.app/Contents/Resources/codex \
      -c 'model_catalog_json="/dev/stdin"' \
      debug models

The command exits with the missing-field error above.

As a control, the catalog from the matching client tag loads successfully and returns all eight models:

curl -fsSL https://raw.githubusercontent.com/openai/codex/rust-v0.148.0-alpha.9/codex-rs/models-manager/models.json \
  | /Applications/ChatGPT.app/Contents/Resources/codex \
      -c 'model_catalog_json="/dev/stdin"' \
      debug models

Environment:

Codex App bundle version: 26.810.52044
Codex App build: 6662
Embedded Codex binary: codex-cli 0.148.0-alpha.9
Platform: Darwin 24.6.0 arm64 arm

What is the expected behavior?

One of the following contracts should be explicit:

  1. model_catalog_json catalogs remain backward compatible with supported released clients.
  2. Catalogs are versioned and users are directed to the catalog matching their Codex release.
  3. Cross-version catalogs are unsupported, but the configuration documentation and parse error clearly explain the client/catalog version mismatch.

The current configuration reference describes model_catalog_json as an optional catalog path, but does not state that a file copied from main must match the client release:

https://developers.openai.com/codex/config-reference

Additional information

The incompatibility appeared when supports_parallel_tool_calls was intentionally removed from both ModelInfo and the bundled catalog:

https://github.com/openai/codex/commit/86b1123ff6b5d089a146be4e603a324cf454223a

The failing current catalog is:

https://github.com/openai/codex/blob/main/codex-rs/models-manager/models.json

The working version-matched catalog is:

https://github.com/openai/codex/blob/rust-v0.148.0-alpha.9/codex-rs/models-manager/models.json

Re-adding the removed field unconditionally may not be the right fix because the field was intentionally retired. A documented version contract, a versioned catalog/schema, or a compatibility strategy would avoid treating the current main file as directly reusable by older released clients.

View original on GitHub ↗

1 Comment

jdcodes1 · 11 days ago

Small addendum that narrows the breakage class and suggests a low-cost compatibility contract.

The reason this particular retirement broke released clients is that on the shipped client, the field was declared requiredrust-v0.148.0-alpha.9 has:

pub truncation_policy: TruncationPolicyConfig,
pub supports_parallel_tool_calls: bool,   // no #[serde(default)]

(openai_models.rs L412 on that tag), while most other bools in ModelInfo carry #[serde(default)] / #[serde(default = "default_true")] and would have tolerated the removal silently. Serde already ignores unknown fields here, so the other direction — an old catalog on a new client — is safe; the only breaking combination is "field removed from catalog while some supported client still declares it required".

That suggests a cheaper contract than versioned catalogs (option 2 in the issue):

  1. Every field in the catalog-facing structs (ModelInfo and friends) gets a serde default at introduction time — i.e., treat "required field in the catalog schema" as a lint failure. Then removals from models.json are non-breaking for all past clients that followed the rule, and additions are non-breaking for old clients automatically.
  2. For fields that are already required in shipped clients (like this one), retirement needs a deprecation window: keep emitting the value in models.json (it's dead weight for new clients, which ignore it) until the oldest supported client version has the default annotation.

That would make option 1 from the issue ("catalogs from main remain loadable by supported released clients") mostly enforceable mechanically — a CI check that deserializes the current models.json with the oldest-supported release's schema would catch this exact regression.