# Codex 새 모델이 안 보일 때: 공식 Model Catalog 동기화 + Sub2API 원본 이름 전달

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

Codex에 새 모델이 나왔을 때 저는 이제 바로 클라이언트 설정부터 고치지 않습니다. 더 안정적인 순서는 **공식 모델 catalog에 실제 slug가 존재하는지 먼저 확인하고**, 그다음 로컬 catalog와 클라이언트 버전 조건을 보고, 마지막에 Sub2API가 그 모델명을 upstream까지 그대로 전달하는지 확인하는 것입니다.

이 계층들은 쉽게 섞입니다. 모델이 선택 목록에 없다고 해서 반드시 Sub2API 문제는 아닙니다. 반대로 모델을 선택할 수 있는데 요청이 실패한다고 해서 로컬 Codex 설정 문제라고 단정할 수도 없습니다. “표시”, “라우팅”, “upstream 호출”을 분리하면 훨씬 빨리 원인을 찾을 수 있습니다.

제가 지금 고정해서 쓰는 순서는 다음과 같습니다.

```text
OpenAI 공식 models.json
        ↓
실제 모델 slug 확인
        ↓
로컬 codex-models.json에 동기화
        ↓
minimal_client_version 확인
        ↓
Codex / Desktop 재시작
        ↓
Sub2API 허용 목록과 모델 매핑 확인
        ↓
Responses API에서 원본 이름 그대로 전달
        ↓
요청 모델 / upstream 모델 비교
```

현재 공식 catalog의 `gpt-6.1-sol`을 예로 들면 공식 항목에는 다음 정보가 명시돼 있습니다.

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

그래서 첫 단계는 언제나 “내가 모델 이름을 만들어 넣는 것”이 아니라 **공식 catalog에서 실제 항목을 확인하는 것**입니다.

## slug 하나만 손으로 추가하지 말고 공식 항목 전체를 동기화한다

제 `~/.codex/config.toml`은 로컬 모델 catalog를 가리킵니다.

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

새 모델이 보이면 먼저 기존 파일을 백업합니다.

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

그다음 공식 최신 catalog를 가져옵니다.

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

예를 들어 이번에 확인할 모델이 다음과 같다고 합시다.

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

먼저 공식 파일에 실제로 있는지 확인합니다.

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

항목이 확인되면 간소화된 object를 직접 작성하지 않고 **공식 object 전체를 로컬 catalog에 병합**합니다.

```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
```

이렇게 하면 `slug`만 복사되는 것이 아니라 display name, reasoning level, context 제한, tool capability, 버전 조건 같은 metadata도 같이 유지됩니다. 나중에 공식 필드가 바뀌어도 직접 만든 축약본을 계속 보수할 필요가 없습니다.

파일을 바꾼 뒤에는 Codex / ChatGPT Desktop을 **완전히 종료한 뒤 다시 실행**하는 편이 좋습니다. macOS에서는 보통 `⌘ + Q`로 기존 프로세스와 이전 catalog가 남지 않게 합니다.

## catalog에 있는데도 모델이 보이지 않는 이유

이때는 로컬 항목부터 다시 확인합니다.

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

항목이 이미 존재한다면 다음 확인 대상은 현재 클라이언트 버전과 `minimal_client_version`입니다.

예를 들어 현재 공식 `gpt-6.1-sol` 항목의 `minimal_client_version`이 `0.153.0`이라면, 이 값은 클라이언트 측 compatibility gate로 볼 수 있습니다. 모델 metadata는 존재하더라도 버전이 너무 낮은 클라이언트가 모델을 정상 노출하지 않을 수 있습니다.

제가 쓰는 우선순위는 이렇습니다.

```text
먼저 Codex 업그레이드
    ↓
클라이언트 재시작
    ↓
모델 표시 여부 확인
    ↓
그래도 원인 분석이 필요할 때만 로컬 버전 조건 점검
```

“모델이 안 보이는 이유가 로컬 버전 gate인지”를 진단하는 목적으로는 백업 후 특정 모델의 값을 일시적으로 `null`로 바꿔 볼 수 있습니다.

```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
```

하지만 이 단계를 기본 동작으로 두지는 않습니다. **선택 목록에 나타나는 것과 현재 클라이언트가 실제로 그 모델과 호환되는 것은 다른 문제**입니다. 이 변경은 UI 숨김 원인을 진단하는 용도에 가깝지, 진짜 compatibility requirement를 우회하는 방법으로 보면 안 됩니다.

## Codex에 모델이 보인 뒤에는 Sub2API를 별도로 확인한다

이 부분이 예전에는 가장 쉽게 빠뜨리던 계층이었습니다.

로컬 Codex에서 `gpt-6.1-sol`을 선택할 수 있다는 사실은 클라이언트가 그 모델 이름을 알고 있다는 뜻일 뿐입니다. 실제 요청이 나갈 때는 **Sub2API가 그 slug를 허용하고 upstream까지 올바르게 전달하는지** 따로 봐야 합니다.

제 구성에서는 공식 모델이 실제 slug를 갖고 있다면 가능한 한 원본 이름 그대로 전달합니다.

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

다음 네 곳을 순서대로 확인합니다.

- OpenAI 그룹의 모델 allowlist에 새 모델이 허용돼 있는가
- 계정 수준 모델 allowlist가 별도로 막고 있지 않은가
- 모델 mapping이 새 slug를 예전 모델로 강제로 바꾸고 있지 않은가
- “알 수 없는 모델 기본 mapping”이 새 모델을 다른 slug로 fallback하고 있지 않은가

공식 실제 slug가 이미 있다면 저는 별칭을 추가하지 않는 편입니다. mapping이 적을수록 로그가 읽기 쉽고 나중에 문제를 추적하기도 쉽습니다.

Codex provider는 계속 Responses API를 사용합니다.

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

정말 별칭이 필요해서 다음처럼 쓰고 싶다면,

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

그 관계를 Sub2API의 **명시적인 routing rule**로 관리하는 편이 낫습니다. 기본 fallback이 몰래 대신하도록 두면 나중에 실제 upstream 모델을 확인하기 어렵습니다.

## 최종 검증은 “보인다”가 아니라 요청 모델과 upstream 모델이 같은지 확인하는 것

먼저 로컬 catalog에서 모델이 존재하는지 확인합니다.

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

그다음 실제 요청을 한 번 보내고 Sub2API 로그를 확인합니다.

제가 보고 싶은 결과는 다음과 같습니다.

```text
요청 모델 = gpt-6.1-sol
upstream 모델 = gpt-6.1-sol
요청 성공
```

이 세 가지가 맞아야 전체 연결이 끝났다고 봅니다.

```text
Codex 선택 목록에 표시됨
        +
요청 모델이 올바름
        +
Sub2API upstream 모델이 올바름
        +
실제 호출 성공
```

문제가 생기면 계층별로 나눠 봅니다.

```text
선택 목록에 없음
→ catalog / 클라이언트 버전

선택은 되지만 Sub2API가 거부
→ allowlist

요청 모델은 맞는데 upstream 모델이 다름
→ mapping / 기본 fallback

upstream 모델도 맞는데 실패
→ upstream 권한, 계정 capability, API compatibility
```

이 흐름을 만들어 둔 뒤 가장 좋은 점은 OpenAI가 다음 Codex 모델을 추가하더라도 “어디를 바꿔야 하지?”를 처음부터 다시 고민하지 않아도 된다는 것입니다. **공식 catalog를 source of truth로 보고, 로컬 표시 문제와 Sub2API routing 문제를 분리해서 검증**하면 대부분 빠르게 원인을 좁힐 수 있습니다.

Codex를 직접 관리하는 프록시 endpoint에 연결하고 있다면 [Codex를 CLIProxyAPI로 프록시하는 방법: Docker 배포, OAuth 로그인, 클라이언트 설정](/ko/posts/cliproxyapi-codex-docker-proxy/)도 함께 볼 수 있습니다. Codex가 왜 점점 단순한 채팅 도구가 아니라 개발 환경에 가까워지는지 먼저 보고 싶다면 [새 Codex는 무엇이 달라졌나: 이제 왜 개발 환경에 더 가까운가](/ko/posts/what-changed-in-the-new-codex/)가 이어집니다.
