Codex CLI 报 model_not_found?用 config.toml、/models 和真实模型 ID 逐层定位
Codex CLI 报 model_not_found?用 config.toml、/models 和真实模型 ID 逐层定位
如果 Codex CLI 已经能启动,但执行任务时提示 `model_not_found`、`unknown model`,最容易走错的一步是继续换 API Key。**在本文的本地夹具里**,认证通过后才进入模型匹配分支:请求已经到达端点,但 `model` 字段不是该夹具当前可识别的模型 ID。不同线上 provider 的错误顺序可能不同,仍应以目标服务的错误体和文档为准。
本文只解决一个问题:Codex CLI 自定义 provider 配好后,如何确认它读到了哪份配置、请求发到哪里,以及模型 ID 应该怎么改。实测范围是 Codex CLI `0.144.1`,不是 Codex Desktop,也不是 ChatGPT Desktop。下面的 HTTP 结果来自 `127.0.0.1` 的脱敏本地夹具,目的是复现错误分支,不代表任何线上服务的模型目录。
先按这 5 步跑一遍
1. 确认版本和配置位置
codex --version
ls -l ~/.codex/config.toml
本文实测版本是 `codex-cli 0.144.1`。当前官方配置参考把用户级配置放在 `~/.codex/config.toml`。本篇只验证这一用户级 provider 入口;不要根据本文把所有版本、所有项目级 `.codex/config.toml` 的行为一概而论,项目级配置是否支持及其作用范围应按你当前版本的官方文档核对。
2. 写一份最小 provider 配置
先备份已有配置,再只增加一个独立 provider。Key 不直接写进文章、截图或 shell history,用环境变量名引用:
model = "fixture-model-v1"
model_provider = "fixture"
[model_providers.fixture]
name = "fixture"
base_url = "http://127.0.0.1:PORT/v1"
env_key = "CODEX_FIXTURE_API_KEY"
wire_api = "responses"
实际使用时,把 `PORT` 换成你的端点端口,把 `fixture-model-v1` 换成目标服务模型目录中真实存在的 ID。`model_provider` 必须和 `[model_providers.fixture]` 的名字对应;不能写成展示名称、产品昵称或你自己猜的别名。
3. 先验证配置能被解析
codex --strict-config --help >/tmp/codex-config-check.txt
echo $?
成功信号是退出码 `0`。如果这里就失败,先修 TOML 语法、引号、区块层级和 provider 名称,不要先排查网络。本文的本地实测同时打印了 `CODEX_VERSION` 和 `STRICT_CONFIG_EXIT=0`。
4. 从模型目录复制 ID
export BASE_URL="https://your-openai-compatible-endpoint/v1"
curl -sS -H "Authorization: Bearer $CODEX_FIXTURE_API_KEY" \
"$BASE_URL/models" | jq '.data[] | .id'
把输出中的 `id` 原样复制到 `model`。大小写、连字符、版本后缀都不要自行改写。若目标端点不提供 `/models`,就以它的官方文档或控制台模型目录为准;不要把一个端点的模型名带到另一个端点。
5. 只发一条最小请求
codex exec --ephemeral "只返回 CODEX_MODEL_OK"
在你的真实端点上,成功信号是终端出现预期短句,且没有 `model_not_found`。先用短提示词验证模型路由,再恢复完整工作流,这样更容易把模型问题和项目权限、工具调用问题分开。本文实际保存的本地证据是后面的 HTTP fixture 对照,不把这条命令写成已经在你的端点上执行过。
本地实测:错误、成功和路径重复
为避免把“应该返回什么”写成经验判断,我在本地启动了一个只监听 `127.0.0.1` 的 OpenAI-compatible fixture。它只认识 `fixture-model-v1`:
GET /v1/models -> 200,返回 fixture-model-v1
POST /v1/responses model=wrong -> 404,code=model_not_found
POST /v1/responses model=fixture...-> 200,返回 CODEX_MODEL_OK
POST /v1/v1/responses -> 404,code=not_found
这里有两个容易混淆的 404。第一种是模型 ID 不存在:路径是对的,服务端已经进入模型路由,错误体通常会带 `model_not_found`。第二种是 Base URL 或客户端自动拼接错误:路径变成重复的 `/v1/v1`,错误体是路由不存在。它们的修复动作完全不同。
按错误信号分层修复
`strict_config_exit` 非 0
这还没到网络层。检查 `~/.codex/config.toml` 是否是有效 TOML,`model_provider` 是否指向真实的 provider 区块,字符串是否使用成对引号。把整份配置删除重写通常会丢掉其他设置,优先只修当前区块。
`doctor` 看不到目标 provider
检查 Codex 使用的配置根目录,以及 `model_provider` 的拼写。不要把 `auth.json` 的登录状态当成自定义 provider 配置;它们解决的是不同层的问题。本文没有读取或输出任何凭据内容,只检查配置结构和脱敏诊断信号。
`401` 或 `invalid_api_key`
这时先检查认证头、Key 环境变量名和端点是否一致。不要因为匿名 `/models` 返回 401 就立即判定 Key 失效:匿名探测只能说明路由存在且要求认证。用同一 Key 对 `/models` 和最小请求分别验证,才能知道是缺 Key、Key 归属错误还是权限范围不够。
`404 model_not_found`
这条错误的第一动作是重新取得真实模型目录,不是改 Base URL。把模型 ID 分成三类看:服务端返回的 `id`、客户端界面展示名、你在文章或旧配置里看到的别名。只有第一类可以直接放进请求的 `model` 字段。若 `/models` 成功但模型调用仍报错,保存完整错误体中的 `model`、`code` 和请求路径,再核对是否切到了另一个 provider。
`404 not_found` 且路径出现 `/v1/v1`
这是路径边界问题。若客户端会自动拼接 `/v1`,Base URL 就不要再写一遍;若文档要求 Base URL 已包含 `/v1`,则按该客户端的最终请求路径验证。不要只看配置文件字符串,要用日志或最小请求确认最终 URL。
`200` 但仍认为失败
不要只看 HTTP 状态码。检查响应是否有预期的 Responses 结构、非空输出和可解析的模型字段。一个代理可以返回 `200` 加错误 JSON,也可能把不兼容的 Chat Completions 字段塞进 Responses 路径。此时问题已经从模型目录转到协议契约,需要保存脱敏响应再检查字段。
不要把本地夹具当成线上模型目录
本地 fixture 只能证明排错顺序和错误分类是可执行的:正确模型 200、错误模型 `model_not_found`、重复路径 404。真正接入某个在线 provider 时,仍要重新读取它的模型目录、认证方式和 wire API。不同服务的模型别名、权限和可用范围可能变化,不能把本文的 `fixture-model-v1` 复制到生产配置。
本文只披露一次测试环境:示例使用作者维护的 OpenAI-compatible 测试端点之一,读者可以替换为自己的服务;正文不依赖注册、充值、优惠或购买才能完成排错。
最后保留这张检查表
[ ] 我改的是 ~/.codex/config.toml,而不是项目内同名文件
[ ] model_provider 与 model_providers 区块名称完全一致
[ ] base_url 没有让客户端重复拼出 /v1/v1
[ ] /models 返回的 id 与 model 字段完全一致
[ ] 401、model_not_found、not_found 被分开记录
[ ] 最小请求返回可解析的预期内容
[ ] 日志中没有明文 Key、Cookie 或用户数据
如果只记住一句话:`model_not_found` 先查“模型目录和最终请求路径”,不要把它当成“再换一个 Key”问题。配置层、认证层、模型层和协议层分开验证,才能让下一次排错有明确动作。
更多推荐

所有评论(0)