Codex 400 Bad Request 错误解决方法
Codex 400 Bad Request 错误解决方法
Codex 调接口或在命令行工具里跑任务时遇到 400 Bad Request,一般不是网络断了,也不是服务端完全不可用,而是请求本身有问题。先不要急着换模型、重装环境,建议先查三件事:请求参数、模型名称、消息格式。很多 400 错误就是这几类原因。
我这边常见场景是:本地脚本调用 Codex 相关接口,或者在 Cursor、VS Code、自动化脚本里接入 API,前一天还能用,改了一点参数后突然返回 400。错误信息可能很短,只看到:
### token云桥中转 0029.org ###
400 Bad Request
也可能带有更具体的提示,例如:
{
"error": {
"message": "Invalid request body",
"type": "invalid_request_error",
"param": null,
"code": null
}
}
如果返回里有 message 字段,一定优先看它,比单纯看 HTTP 状态码更有价值。
一、先确认是不是请求体格式错误
400 最常见原因就是 JSON 写错、字段名写错、类型不对。比如某些参数要求是数组,你传了字符串;要求是数字,你传了带单位的字符串;或者 JSON 末尾多了逗号。
可以先用最小请求验证接口是否能通。下面是一个通用的 curl 检查方式,注意把模型名和地址换成你实际使用的:
curl -i -X POST "https://api.example.com/v1/chat/completions" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "codex-mini-latest",
"messages": [
{
"role": "user",
"content": "请输出 hello"
}
]
}'
如果最小请求能成功,说明账号、网络、基础地址大概率没问题,后面再对比你原来的完整请求。排查时不要一次改太多参数,先删到最小,再一项一项加回去。
二、检查模型名称是否写错
模型名称写错也经常返回 400 或类似的参数错误。比如把 codex-mini-latest 写成 codex-mini,或者复制配置时多了空格、换行。建议打印最终发送出去的请求体,而不是只看配置文件。
console.log(JSON.stringify(payload, null, 2));
在 Python 里也可以这样看:
import json
print(json.dumps(payload, ensure_ascii=False, indent=2))
重点看 model 字段是否和你当前服务支持的名称一致。如果你走的是第三方中转或企业网关,不同网关可能会做模型别名映射,不能直接照搬别处的示例。
三、检查 messages 结构和 role
不少 400 是因为消息结构不合法。常见错误包括:
messages不是数组,而是对象。content为空,或者传了null。role写错,例如写成assistant_user。- 多模态内容格式混用,文本接口里塞了图片对象。
- 历史消息太长,拼接时把未序列化的对象直接传进去了。
一个相对稳妥的结构应该类似这样:
{
"model": "codex-mini-latest",
"messages": [
{
"role": "system",
"content": "你是一个代码助手。"
},
{
"role": "user",
"content": "帮我解释这段 JavaScript 代码。"
}
]
}
如果你是从业务日志、数据库里取历史对话,建议在发送前做一次清洗,过滤掉空内容:
messages = [
m for m in messages
if m.get("role") in ["system", "user", "assistant"] and m.get("content")
]
四、排查 base_url 和鉴权头
虽然鉴权失败更多是 401,但实际项目里也见过网关把鉴权异常包装成 400 的情况。尤其是配置了代理、中转地址、企业 API 网关时,base_url 末尾路径很容易写错。
常见问题有:
base_url多写了一层/v1,最终请求变成/v1/v1/chat/completions。- 环境变量没有生效,程序拿到的是空 key。
Authorization头缺少Bearer。- 代理地址能访问,但不支持当前接口路径。
可以直接打印环境变量确认:
echo $OPENAI_API_KEY
echo $OPENAI_BASE_URL
Windows PowerShell 下用:
echo $env:OPENAI_API_KEY
echo $env:OPENAI_BASE_URL
如果你国内网络访问不稳定,或者团队里多人共用配置,实践里我会建议把 API 接入统一放到中转服务上管理。比如 token云桥AI中转站 0029.org,适合用来做密钥隔离、额度控制和统一 base_url 配置,排错时也方便看请求日志。不过接入前还是要确认它支持你正在调用的模型和接口格式。
五、检查参数是否超出限制
有些参数不是格式错,而是值不被接受。比如 temperature 超出范围、max_tokens 过大、传了当前模型不支持的字段。常见写法如下:
{
"model": "codex-mini-latest",
"messages": [
{
"role": "user",
"content": "写一个快速排序"
}
],
"temperature": 0.2,
"max_tokens": 1024
}
排查这类问题时,建议先去掉所有可选参数,只保留 model 和 messages。如果能成功,再逐个加回 temperature、max_tokens、stream 等参数。这样比盯着一大段 JSON 猜要快很多。
六、用脚本做一次最小验证
下面给一个 Python 最小验证脚本,适合确认当前 key、base_url、模型名是否可用:
import os
import requests
api_key = os.getenv("OPENAI_API_KEY")
base_url = os.getenv("OPENAI_BASE_URL", "https://api.example.com/v1")
url = base_url.rstrip("/") + "/chat/completions"
payload = {
"model": "codex-mini-latest",
"messages": [
{"role": "user", "content": "输出 hello"}
]
}
resp = requests.post(
url,
headers={
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json"
},
json=payload,
timeout=30
)
print(resp.status_code)
print(resp.text)
如果这里返回 200,说明基础链路没问题。再把业务代码里的 payload 打印出来,对比两边差异。重点看路径、模型名、messages、可选参数这几块。
七、修复后怎么验证
不要只看一次请求成功就结束,最好按下面顺序验证:
- 用最小请求验证 200 返回。
- 加入真实业务提示词,确认不是内容为空或格式异常。
- 加入历史消息,确认数组拼接没有脏数据。
- 开启流式输出时,再单独验证
stream参数。 - 查看服务端日志,确认没有隐藏的 400 重试。
如果项目里有自动化测试,可以加一个接口连通性检查,避免配置改动后上线才发现 400:
def test_codex_api_health():
assert call_codex("ping") is not None
八、避免再次出现
实际维护中,避免 400 反复出现,关键是把请求构造收口。不要在多个地方手写 JSON,最好封装一个统一的调用函数,并在发送前做参数校验。模型名、base_url、超时时间放到配置里,改动时能追踪。
另外,日志里至少记录状态码、错误消息、请求 ID、模型名,不建议记录完整用户内容,尤其是生产环境。这样既方便排错,也能减少敏感信息泄露风险。
总结
Codex 400 Bad Request 大多是请求格式、模型名称、消息结构或参数范围的问题。排查时先用最小请求确认链路,再逐步加回业务参数;不要一上来重装依赖或更换环境。把请求体打印出来,对照接口要求检查,通常很快能定位到具体字段。
更多推荐


所有评论(0)