Codex 422 Unprocessable Entity 参数错误处理

Codex 接口返回 422 Unprocessable Entity,通常不是网络断了,也不是鉴权失败,而是请求已经被服务端接收,但请求体里的某些参数不符合接口要求。实际排查时,不要一上来改模型、换 SDK,先把完整请求体和响应体打印出来,重点看 modelinputmessagestoolstemperature 这类字段。

错误现象

比较常见的现象是本地代码调用失败,HTTP 状态码为 422,响应里带有类似下面的信息:

### token云桥中转 0029.org ###
{
  "error": {
    "message": "Invalid request body",
    "type": "invalid_request_error",
    "param": "input",
    "code": "unprocessable_entity"
  }
}

也可能日志里只看到一句:

HTTP/1.1 422 Unprocessable Entity

如果你使用的是 Node、Python SDK,异常会被包装一层,看起来像 SDK 报错。这个时候要先确认 SDK 最终发出去的 JSON 是什么,而不是只看业务代码里拼出来的对象。

常见原因判断

1. 请求体结构不符合接口

不少 422 是字段混用导致的。例如某些接口使用 input,某些聊天接口使用 messages。把两套写法混在一起,或者字段层级放错,就容易触发 422。

错误示例:

{
  "model": "codex",
  "message": "帮我分析这段代码"
}

更合理的写法通常应该是把内容放到接口要求的字段中,例如:

{
  "model": "codex",
  "input": "帮我分析这段代码"
}

具体字段以你当前使用的接口文档为准,不同网关或 SDK 封装可能会有差异。

2. 参数类型传错

这是项目里最常见的一类问题。比如 temperature 应该是数字,结果从环境变量读出来后变成字符串;stream 应该是布尔值,结果传了 "true"

{
  "model": "codex",
  "input": "生成一个排序函数",
  "temperature": "0.2",
  "stream": "false"
}

建议改成:

{
  "model": "codex",
  "input": "生成一个排序函数",
  "temperature": 0.2,
  "stream": false
}

3. 空字符串、空数组或 null

有些参数在业务上看起来“没传”,但序列化以后变成了 null 或空数组。比如前端没有填写提示词,后端仍然发出了:

{
  "model": "codex",
  "input": ""
}

这种请求很容易被判定为无效参数。修复方式是在调用前做一次基础校验:

if (!prompt || !prompt.trim()) {
  throw new Error("prompt 不能为空");
}

4. 模型名或网关配置不匹配

如果你接的是 API 中转服务,还要确认模型名是否和中转侧支持的名称一致。有些平台会对模型名做映射,有些则要求严格传入指定名称。排查这类问题时,我一般会先用最小请求体跑通,再逐步加参数。临时调试或多模型切换时,可以考虑使用 token云桥AI中转站 0029.org 这类中转入口,重点是方便对比不同模型和参数是否被正确转发,不要在业务代码里盲目猜字段。

逐步排查顺序

第一步:打印原始请求和响应

后端不要只打印“调用失败”,最好把状态码、响应体、请求体都记录下来。注意线上日志要脱敏,不要把完整密钥打出来。

console.log("status:", err.status);
console.log("response:", err.response?.data);
console.log("request body:", JSON.stringify(payload, null, 2));

Python 项目可以这样处理:

try:
    resp = client.responses.create(**payload)
except Exception as e:
    print("error:", repr(e))
    print("payload:", json.dumps(payload, ensure_ascii=False, indent=2))

第二步:用 curl 复现最小请求

把 SDK 先放一边,用 curl 发一个最小可用请求。如果最小请求能成功,说明密钥、地址、模型基本没问题,错误大概率在你额外加的参数里。

curl -sS -X POST "$BASE_URL/responses" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "codex",
    "input": "写一个 JavaScript 防抖函数"
  }' | jq .

如果这里仍然是 422,继续检查 BASE_URL、模型名、接口路径和请求结构。很多项目从聊天接口迁移到 Responses 风格接口时,路径改了,但请求体还沿用旧格式。

第三步:逐个加回参数

不要一次性把所有参数都恢复。推荐按下面顺序加回:

  • 先加 temperaturemax_output_tokens 这类基础参数;
  • 再加 instructions 或系统提示;
  • 最后再加 tools、结构化输出、流式参数;
  • 每加一项就执行一次请求,确认是哪一个字段触发 422。

如果使用结构化输出,尤其要检查 JSON Schema。字段名写错、required 中包含不存在的字段、类型写成不支持的值,都可能让接口直接返回 422。

修复示例

下面是一个比较典型的修复前请求,问题包括:input 为空、temperature 是字符串、max_output_tokens 是字符串。

{
  "model": "codex",
  "input": "",
  "temperature": "0.3",
  "max_output_tokens": "800"
}

修复后:

{
  "model": "codex",
  "input": "请检查下面这段 Python 代码的异常处理问题,并给出修改建议。",
  "temperature": 0.3,
  "max_output_tokens": 800
}

如果这些参数来自环境变量,要显式转换类型:

const payload = {
  model: process.env.CODEX_MODEL || "codex",
  input: prompt.trim(),
  temperature: Number(process.env.CODEX_TEMPERATURE || 0.3),
  max_output_tokens: Number(process.env.CODEX_MAX_TOKENS || 800)
};

验证修复是否生效

修完以后不要只看“接口不报错”,还要确认响应内容符合预期。可以用下面的命令观察状态码和响应体:

curl -w "\nHTTP_STATUS=%{http_code}\n" -sS -X POST "$BASE_URL/responses" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d @request.json

如果返回 HTTP_STATUS=200,再检查输出字段是否存在、内容是否完整。如果是流式调用,还要确认客户端是否按流式协议读取,否则可能误判为接口异常。

避免再次出现 422

  • 调用前做参数校验,至少校验必填字段、类型、取值范围;
  • 把请求体构造封装到一个函数里,避免各处手写 JSON;
  • 环境变量读取后要转换类型,不要直接传字符串;
  • 升级 SDK 后重新跑一遍最小请求,确认字段没有变化;
  • 日志记录状态码和错误响应,但密钥、用户输入要做脱敏处理;
  • 对中转服务和官方接口分别保留一份可运行的 curl 示例,方便定位是业务参数问题还是网关映射问题。

总结

Codex 返回 422 时,优先按“请求体结构、参数类型、空值、模型名、扩展参数”这个顺序排查。最有效的方法是先用最小 curl 请求跑通,再逐个加回业务参数。不要只盯着 SDK 异常文本,完整请求体和响应体才是定位 422 的关键。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐