# New Codex Model Not Showing? Sync the Official Catalog and Sub2API

> Canonical: https://vxi.me/en/posts/codex-new-model-catalog-sub2api/
> Locale: en-us
> Published: 2026-10-05
> Updated: 2026-10-05
> Markdown: https://vxi.me/en/posts/codex-new-model-catalog-sub2api.md

When a new Codex model ships, I no longer start by editing random client settings. The reliable path is to verify the official model slug first, sync the full upstream catalog entry, check the client version gate, and only then move downstream into Sub2API routing.

Those layers are easy to mix together. A model missing from the selector does not automatically mean the proxy is wrong. A model that appears locally but fails at request time does not automatically mean the local Codex config is wrong either.

The workflow I keep now is:

```text
OpenAI official models.json
        ↓
verify the real model slug
        ↓
sync the full entry into codex-models.json
        ↓
check minimal_client_version
        ↓
restart Codex / Desktop
        ↓
check Sub2API allowlists and mappings
        ↓
pass the real slug through Responses API
        ↓
compare requested model and upstream model
```

The current official catalog is a good example. It now contains `gpt-6.1-sol` with metadata including:

```text
slug: gpt-6.1-sol
display_name: GPT-6.1-Sol
minimal_client_version: 0.153.0
supported_in_api: true
```

That is why I treat the official catalog as the source of truth instead of inventing a model name locally.

## Sync the full official entry instead of hand-writing a slug

My `~/.codex/config.toml` points Codex at a local model catalog:

```toml
model_catalog_json = "~/.codex/codex-models.json"
```

Before changing it, I create a backup:

```bash
cp ~/.codex/codex-models.json \
  ~/.codex/codex-models.json.bak.$(date +%Y%m%d-%H%M%S)
```

Then I fetch the latest official catalog:

```bash
curl -fsSL \
  https://raw.githubusercontent.com/openai/codex/main/codex-rs/models-manager/models.json \
  -o /tmp/codex-models-official.json
```

For this example:

```bash
MODEL="gpt-6.1-sol"
```

I verify that the model really exists upstream:

```bash
jq --arg MODEL "$MODEL" '
  .models[]
  | select(.slug == $MODEL)
  | {slug, display_name, minimal_client_version, supported_in_api}
' /tmp/codex-models-official.json
```

Once it exists, I merge the complete official object into the local file:

```bash
jq --arg MODEL "$MODEL" \
   --slurpfile official /tmp/codex-models-official.json '
  .models = (
    [.models[] | select(.slug != $MODEL)]
    +
    [$official[0].models[] | select(.slug == $MODEL)]
  )
' ~/.codex/codex-models.json > ~/.codex/codex-models.json.new

jq empty ~/.codex/codex-models.json.new &&
mv ~/.codex/codex-models.json.new ~/.codex/codex-models.json
```

The reason I copy the full object is simple: the slug is only one field. The official entry also carries display metadata, reasoning support, tool behavior, context limits, client-version requirements, and other model capabilities. Keeping that whole object avoids maintaining a hand-edited shadow catalog that slowly drifts away from upstream.

After the update, fully quit Codex or ChatGPT Desktop and reopen it. On macOS, I use `⌘ + Q` so the old process does not keep stale model metadata around.

## If the entry exists but the model still does not appear

At that point I inspect the local metadata directly:

```bash
jq --arg MODEL "$MODEL" '
  .models[]
  | select(.slug == $MODEL)
  | {slug, display_name, minimal_client_version}
' ~/.codex/codex-models.json
```

For `gpt-6.1-sol`, the current official catalog sets `minimal_client_version` to `0.153.0`. That gives you a concrete compatibility check instead of guessing why the UI is hiding a model.

My order is:

```text
update Codex
    ↓
restart the client
    ↓
check the selector again
    ↓
only then debug the local version gate
```

For troubleshooting, you can temporarily clear the gate for one model in a backed-up local catalog:

```bash
jq --arg MODEL "$MODEL" '
  (.models[]
    | select(.slug == $MODEL)
    | .minimal_client_version
  ) = null
' ~/.codex/codex-models.json > ~/.codex/codex-models.json.new &&
jq empty ~/.codex/codex-models.json.new &&
mv ~/.codex/codex-models.json.new ~/.codex/codex-models.json
```

I treat that as a diagnostic step, not a normal upgrade strategy. Making a model visible in the selector does not prove that an older client fully supports it.

## Once Codex sees it, Sub2API still has its own routing layer

This is the part that is easy to forget.

A local Codex selector showing `gpt-6.1-sol` only proves that the client knows the model name. The actual request still has to make it through Sub2API and reach the upstream model you intended.

In my setup, real official model slugs are passed through unchanged:

```text
Codex:    gpt-6.1-sol
             ↓
Sub2API:  gpt-6.1-sol
             ↓
OpenAI:   gpt-6.1-sol
```

I check four places:

- the OpenAI group model allowlist
- any account-level model allowlist
- explicit model mappings
- the fallback/default mapping for unknown models

If the upstream already exposes a real slug, I prefer to keep that slug intact. Fewer aliases make logs easier to read and make routing failures much easier to diagnose.

The Codex provider also stays on the Responses API:

```toml
wire_api = "responses"
```

If you intentionally want an alias such as:

```text
gpt-6 → gpt-6.1-sol
```

then make that an explicit Sub2API mapping. Do not let a generic fallback rule silently decide what a new model should become.

## The final check is model identity end to end

First confirm the local catalog:

```bash
jq -r '.models[].slug' ~/.codex/codex-models.json | grep "$MODEL"
```

Then send a real request and inspect the Sub2API logs.

The clean result is:

```text
requested model = gpt-6.1-sol
upstream model  = gpt-6.1-sol
request success
```

That gives you a much better definition of "the model is working" than simply seeing it in a dropdown:

```text
visible in Codex
        +
requested model is correct
        +
upstream model is correct
        +
real request succeeds
```

If something breaks, the layer usually tells you where to look:

```text
not visible in selector
→ catalog / client version

visible but rejected by Sub2API
→ allowlist

requested model correct, upstream model wrong
→ mapping / fallback

upstream model correct, request still fails
→ upstream permissions, account capability, or API compatibility
```

The useful part of this workflow is that it survives the next model launch. I do not need to remember a special procedure for every new Codex release. I start from the official catalog, keep local visibility separate from proxy routing, and verify the model identity all the way to the upstream request.

If you are already putting Codex behind infrastructure you control, [How to Proxy Codex with CLIProxyAPI: Docker Deployment and Client Setup](/en/posts/cliproxyapi-codex-docker-proxy/) is the natural next read. For the broader product direction, [What Changed in the New Codex: Why It Feels More Like a Dev Environment](/en/posts/what-changed-in-the-new-codex/) gives the bigger picture.
