很多人给 Codex 配第三方 API 时,只在浏览器里打开一下地址:页面能访问、HTTP 返回 200,就认为接口没问题。

结果真正启动 Codex 后,还是会遇到:

404 Not Found
stream disconnected before completion
response.failed event received
Missing environment variable

问题在于:“网站能访问”不等于“Codex 能调用”。

根据当前官方配置文档,Codex 自定义模型提供商使用的是 Responses APIwire_api 目前唯一支持的值也是 responses。因此,一个只兼容 /v1/chat/completions 的平台,即使普通 OpenAI SDK 可以调用,也不代表它能直接供 Codex 使用。

这篇文章不靠“看起来能通”判断,而是用三步依次检查:

  1. /v1/responses 路由和鉴权是否存在;
  2. 非流式 Responses 返回结构是否正确;
  3. SSE 流式事件能否完整结束。

说明:本文包含作者使用和推广的 API 服务示例。路径检查使用 Genvis 的 OpenAI 兼容端点完成;其他服务可以替换 Base URL 和模型名后执行同样的检测。

[TOC]


一、一句话结论

Codex 第三方 API 至少要满足以下条件:

  • 支持 POST /v1/responses
  • 使用 Bearer Token 等客户端可配置的鉴权方式;
  • 返回 Responses API 数据结构,而不是 Chat Completions 结构;
  • stream: true 时返回合法的 text/event-stream
  • 流中能看到 Responses 事件,并最终正常出现完成事件;
  • 配置中的模型名确实存在且当前 Key 有权调用。

仅仅出现 HTTP 200,最多只能证明“某个网页返回了内容”。

二、为什么普通 OpenAI 兼容接口不一定能给 Codex 用

大家最熟悉的旧式对话接口通常是:

POST /v1/chat/completions

Responses API 使用的是:

POST /v1/responses

两者不只是 URL 不同,请求和响应结构也不同。

Chat Completions 常见请求:

{
  "model": "YOUR_MODEL_ID",
  "messages": [
    { "role": "user", "content": "Reply OK" }
  ]
}

Responses API 的最小请求则是:

{
  "model": "YOUR_MODEL_ID",
  "input": "Reply OK"
}

OpenAI 官方 API 参考将创建响应定义为 POST /responses。当前 Codex 配置参考也明确写明,自定义提供商的 wire_api 只有 responses 这一种支持值,而且省略时默认就是它。

所以,下面几种情况都可能发生:

  • /v1/models 可以获取模型列表,但 /v1/responses 是 404;
  • /v1/chat/completions 能对话,但 Codex 无法启动任务;
  • 非流式请求正常,切换 stream: true 后立即断开;
  • 服务返回 HTTP 200,但响应体其实是网站首页 HTML;
  • 能输出文字,却缺少 Codex 需要处理的 Responses 事件。

三、第一步:检查路由和鉴权

先不要急着修改 config.toml,直接从命令行检查端点。

macOS、Linux 或 Git Bash:

export API_BASE="https://genvis.xyz/v1"
export API_KEY="YOUR_API_KEY"
export MODEL="YOUR_MODEL_ID"

PowerShell:

$env:API_BASE="https://genvis.xyz/v1"
$env:API_KEY="YOUR_API_KEY"
$env:MODEL="YOUR_MODEL_ID"

先发送一个真正的 POST 请求:

curl -i --max-time 30 \
  -X POST "$API_BASE/responses" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  --data "{\"model\":\"$MODEL\",\"input\":\"Reply OK\"}"

不要用浏览器地址栏或下面这种请求代替:

curl "$API_BASE/responses"

因为它发送的是 GET,而 Responses 创建接口要求 POSTGET /v1/responses 返回 404 或 405,并不能证明 POST /v1/responses 不存在。

我在无有效 Token 的情况下进行路径检查时,得到的是:

HTTP/1.1 401 Unauthorized
Content-Type: application/json

{"error":{"message":"Invalid token", ...}}

这个结果说明请求已经进入 API 鉴权层,路由不是被前端网页接走;但它仍然不能证明模型可调用,因为还没有通过鉴权和模型检查。

不同返回值可以这样判断:

返回结果 通常说明什么 下一步
401 + JSON 错误 路由大概率存在,但 Key 缺失或错误 检查环境变量、Token 和请求头
403 + JSON 错误 Key 已识别,但权限、分组或访问策略拒绝 检查模型权限、IP 白名单和账号状态
404 + JSON 错误 路径错误,或服务没有实现 Responses API 检查 Base URL 和接口能力
404/200 + HTML 请求打到了官网、反向代理或前端回退页 检查 API 域名、路径和网关配置
400 unknown model 路由和鉴权可能已通过,但模型名不可用 查询模型列表或控制台权限
405 Method Not Allowed 请求方法错误 确认使用 POST

四、第二步:检查非流式响应结构

通过鉴权后,正常的 Responses API 返回不应该是 Chat Completions 的 choices 数组,而应当具有 Responses 对象特征,例如:

{
  "id": "resp_...",
  "object": "response",
  "status": "completed",
  "model": "YOUR_MODEL_ID",
  "output": [],
  "usage": {
    "input_tokens": 0,
    "output_tokens": 0,
    "total_tokens": 0
  }
}

这里不要求字段顺序完全一致,也不要求所有可选字段都出现,但至少需要确认:

  • 响应是 JSON,不是 HTML;
  • object 是 response
  • 有明确的 status
  • 文本位于 Responses 的 output 结构中;
  • 错误时返回结构化错误,而不是网关生成的一段普通网页;
  • Token 用量能正确记录,避免调用成功却无法计费或对账。

如果返回长这样:

{
  "choices": [
    {
      "message": {
        "role": "assistant",
        "content": "OK"
      }
    }
  ]
}

这更像 Chat Completions 返回。普通 SDK 可能可以读取,但不能据此认为 Codex 的 Responses 调用链已经兼容。

五、第三步:检查 SSE 流式事件

Codex 是长时间运行的 Agent 客户端。只验证一次非流式回答还不够,还要验证流式连接。

执行:

curl -N -i --max-time 120 \
  -X POST "$API_BASE/responses" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -H "Accept: text/event-stream" \
  --data "{\"model\":\"$MODEL\",\"input\":\"Reply with exactly OK\",\"stream\":true}"

首先检查响应头:

HTTP/1.1 200 OK
Content-Type: text/event-stream

然后检查事件流。事件内容会因模型和实现不同而变化,但应当符合 Responses API 的 SSE 结构,并能看到类似阶段:

event: response.created
data: {...}

event: response.output_text.delta
data: {...}

event: response.completed
data: {...}

OpenAI 官方文档说明,Responses 流式传输使用 Server-Sent Events,并通过 stream: true 开启。

下面这些情况都值得警惕:

  • 状态码 200,但 Content-Type 是 text/html
  • 返回一整块 JSON 后立即关闭,并不是真正 SSE;
  • 只有文本片段,没有事件类型;
  • 流到一半直接 EOF,没有完成事件;
  • 完成事件中的状态仍是 failed 或 incomplete
  • 服务长时间不发送数据,也没有心跳或合理超时;
  • 网关把上游错误包装成 200 文本。

这类问题常常在 Codex 中表现为:

stream disconnected before completion
response.failed event received

因此,“能返回第一段文字”也不代表整个流式协议合格。

六、确认通过后,再配置 Codex

当前官方配置参考显示,用户级配置文件位于:

~/.codex/config.toml

Windows 通常对应:

%USERPROFILE%\.codex\config.toml

有一个很容易忽略的变化:model_provider 和 model_providers 属于机器本地提供商配置。官方文档说明,把它们写到项目目录的 .codex/config.toml 中会被忽略,因此第三方提供商应放在用户级配置里。

配置示例:

model = "YOUR_MODEL_ID"
model_provider = "genvis"

[model_providers.genvis]
name = "Genvis"
base_url = "https://genvis.xyz/v1"
env_key = "GENVIS_API_KEY"
wire_api = "responses"

wire_api = "responses" 当前可以省略,因为它就是默认值。但排错时建议显式写出来,避免以后看到配置时无法确认协议意图。

不要把 Key 直接写进 TOML:

# not recommended
experimental_bearer_token = "sk-xxxxxxxx"

官方配置参考也明确建议使用 env_key,而不是直接保存 Bearer Token。

macOS/Linux 设置环境变量:

export GENVIS_API_KEY="YOUR_API_KEY"

PowerShell:

$env:GENVIS_API_KEY="YOUR_API_KEY"

配置后要从同一个新终端启动 Codex。VS Code 已经打开时,插件进程可能仍然保留旧环境变量,需要彻底退出后重新打开。

七、Base URL 最常见的三个错误

1. 重复拼接 /v1

如果客户端会在 Base URL 后追加 /responses,那么:

Base URL: https://api.example.com/v1
final URL: https://api.example.com/v1/responses

但如果配置和网关同时追加版本路径,就可能变成:

https://api.example.com/v1/v1/responses

这种情况通常返回 404。

2. 把完整接口写成 Base URL

错误示例:

base_url = "https://api.example.com/v1/responses"

客户端继续追加 /responses 后,可能得到:

https://api.example.com/v1/responses/responses

Base URL 应填公共前缀,而不是某个具体操作接口,除非服务商文档明确要求特殊格式。

3. API 域名和官网域名混用

有些网站会把未知路径统一返回首页,因此错误请求也可能得到 HTTP 200。判断时必须同时看:

  • 最终 URL;
  • HTTP 状态码;
  • Content-Type
  • 响应体;
  • 是否为 SSE;
  • 是否出现完整结束事件。

八、stream disconnected 不一定是换模型能解决

遇到流中断时,很多人的第一反应是切换模型。但可能的故障层至少有五层:

  1. 本机、代理、证书或网络连接;
  2. Codex 客户端版本和配置;
  3. API 网关的超时、缓冲或 SSE 转发;
  4. 上游模型的响应时间和流式实现;
  5. Responses 事件格式不完整。

当前官方 Codex 配置参考中,自定义提供商默认的 SSE 空闲超时是 300000 毫秒,流中断重试次数默认是 5。可以通过:

[model_providers.genvis]
stream_idle_timeout_ms = 300000
stream_max_retries = 5

进行明确配置,但不要把“无限加大超时”当成通用修复。

如果接口每次都在同一个固定时长中断,应检查代理或网关超时;如果第一条事件就解析失败,应优先检查协议;如果只有某个模型失败,应检查模型映射、上下文和上游能力。

九、一张表完成最终判断

检查项 合格表现 不合格表现
请求方法 POST 用浏览器或 GET 测试
路径 /v1/responses 只有 /v1/chat/completions
鉴权 结构化 401/403 或正常通过 HTML 登录页、重定向页面
非流式响应 object: response 只有 choices 或普通文本
流式响应头 text/event-stream text/html、下载文件、普通 JSON
流式过程 Responses SSE 事件 无事件类型、半途 EOF
结束状态 response.completed failedincomplete 或无结束事件
模型权限 当前 Key 可调用 余额有但分组无权限
Codex 配置位置 用户级 ~/.codex/config.toml 把 provider 写进项目级配置
Key 保存 使用 env_key 明文写进配置或 Git

十、总结

判断一个第三方 API 能否供 Codex 使用,不应该问“这个地址能不能打开”,而应该依次问:

  1. POST /v1/responses 是否存在?
  2. 鉴权和模型权限是否通过?
  3. 返回的是 Responses 对象还是 Chat Completions 对象?
  4. stream: true 是否得到真正的 SSE?
  5. 事件流是否完整到达 response.completed
  6. Codex 的 provider 是否写在用户级配置,并从正确环境变量读取 Key?

完成这六项检查,才能把“配置问题”“接口不兼容”“模型无权限”和“流式链路中断”分开。否则不停换模型、改 TOML、重装客户端,很可能只是在重复试错。

如果你正在排查,可以只留下以下信息:操作系统、Codex 版本、最终请求路径、HTTP 状态码、Content-Type 和错误原文。不要发布 API Key、完整请求头或包含业务代码的原始日志。

参考资料

更新记录

  • 2026-08-23:根据当日官方配置参考核对 model_providersenv_keywire_api、SSE 超时和重试配置;完成无有效 Token 的路由与鉴权层检查。
Logo

葡萄城是专业的软件开发技术和低代码平台提供商,聚焦软件开发技术,以“赋能开发者”为使命,致力于通过表格控件、低代码和BI等各类软件开发工具和服务,一站式满足开发者需求,帮助企业提升开发效率并创新开发模式。

更多推荐