切换日光/暗黑模式
两层错误格式不一样(最容易踩的坑)
同一个能力,走不同的路径,报错结构完全不同。先确认你走的是哪一层,再写解析代码。
OpenAI 兼容层 /v1/...:OpenAI 信封
json
{
"error": {
"message": "The model 'grok-99' does not exist",
"type": "wxi_api_error",
"param": "model",
"code": "model_not_found"
}
}error 是对象,文案在 error.message,错误码在 error.code。
原生透传层 /xai/...:xAI 原文
json
{
"code": "imagine:content-moderated",
"error": "Generated image rejected by content moderation.",
"usage": { "cost_in_usd_ticks": 220000000 }
}error 是字符串(就是文案本身),机器可读的原因码在顶层的 code。没有 error.message 这个东西。
两个高频崩溃点
- 在原生层读
error.message→ 拿到undefined,日志里一片空白。原生层要读顶层的error。 - 以为原生层的
error是对象 → 直接取.code会报类型错误。原生层要读顶层的code。
python
# 同时兼容两层的解析写法
def parse_error(resp_json: dict) -> tuple[str | None, str | None]:
err = resp_json.get("error")
if isinstance(err, dict): # 兼容层(OpenAI 信封)
return err.get("code"), err.get("message")
if isinstance(err, str): # 原生层(xAI 原文)
return resp_json.get("code"), err
return resp_json.get("code"), None判断错误优先用
code,不要用message文本——文案会调整,code才是稳定的。
三层对照速查
| OpenAI 兼容层 | 原生透传层 | 异步任务结果 | |
|---|---|---|---|
error 的类型 | 对象 | 字符串 | 对象或字符串 |
| 文案在哪 | error.message | error | error.message / error |
| 错误码在哪 | error.code | 顶层 code | error.code |
| 谁决定文案 | 网关归一化 | xAI 原文 | 网关 / 上游 |
常见错误码
| HTTP | error.code | 含义 | 怎么处理 |
|---|---|---|---|
| 400 | invalid_request_error | 请求体格式错误或参数不合法 | 检查 JSON 是否合法、必填字段是否缺失;param 会指出是哪个字段 |
| 401 | invalid_api_key | Key 无效或未提供 | 确认 Authorization: Bearer <key> 格式,Bearer 后面有且仅有一个空格 |
| 403 | permission_denied | 无权访问该资源 | 该模型可能不在你这个分组的可用范围内,用 GET /v1/models 核对 |
| 404 | model_not_found | 模型 ID 不存在 | 模型名写错了。以 GET /v1/models 的返回为准,不要凭记忆写 |
| 429 | insufficient_quota | 额度不足 | 去控制台充值;也可能是单次请求预估消耗超过剩余额度 |
| 429 | rate_limit | 触发限流 | 降低请求频率,加指数退避重试 |
| 429 | too_many_requests | 并发数超限 | 减少并发;需要更高并发请联系客服或提交工单 |
| 500 | upstream_error | 上游(Grok 官方)返回错误 | 稍后重试;持续出现请带上 request_id 联系客服 |
| 500 | upstream_timeout_no_response | 上游超时无响应 | 稍后重试;长内容生成建议放宽客户端超时 |
| 503 | overloaded | 服务繁忙 | 退避后重试 |
异步任务有自己的错误码
视频这类异步任务在轮询结果里返回的 error.code 是另一套,和上面的 HTTP 错误码不通用:
error.code | 含义 |
|---|---|
invalid_argument | 参数不合法:时长超范围、输入素材有问题、提示词过长、请求模式冲突、被内容审核拦下 |
permission_denied | Key 或团队没有该操作的权限 |
failed_precondition | 该模型/设置不支持这个操作(模型不支持编辑/扩展,或分辨率超了) |
service_unavailable | 上游暂时过载 |
internal_error | 上游内部错误 |
鉴权失败、模型不存在、限流这些错误是在任务创建之前同步返回的,走上面的 HTTP 错误码,不会出现在任务结果的 error.code 里。详见轮询任务状态。
排查问题的三个建议
1. 记下 request_id
每个响应都带一个请求标识。找客服排查时,带上它能定位到具体那一次调用,比描述"昨天晚上有个请求失败了"有用得多。
2. 区分「你的问题」和「我们的问题」
400/401/404/model_not_found→ 请求本身有问题,重试没有用,要先改代码。429/5xx/overloaded→ 暂时性故障,指数退避重试是合理的。
3. 不要对错误无限重试
按错误类型区分:
python
import time, openai
RETRYABLE = {"rate_limit", "too_many_requests", "overloaded",
"upstream_error", "upstream_timeout_no_response"}
def call_with_retry(fn, max_retries=4):
for attempt in range(max_retries):
try:
return fn()
except openai.APIStatusError as e:
code = (e.body or {}).get("error", {}).get("code")
if code not in RETRYABLE or attempt == max_retries - 1:
raise # 不该重试的错误,直接抛
time.sleep(2 ** attempt) # 1s → 2s → 4s → 8s
401、404、model_not_found这类错误重试一万次也还是失败,只会浪费时间并可能触发限流。
