跳到正文
错误处理

错误处理

失败的请求会返回 HTTP 状态码,以及带有错误类型和消息的 JSON 响应体。本页列出每个状态码,展示各 API 格式的错误响应体,并说明哪些错误值得重发。

SDK 会抛出一个错误对象,其中包含状态码和响应体的字段。此请求指定了一个不存在的模型:

import openai
from openai import OpenAI

client = OpenAI(
    api_key="YOUR_API_KEY",
    base_url="https://api.shannon-ai.com/v1",
)

try:
    client.chat.completions.create(
        model="no-such-model",
        messages=[{"role": "user", "content": "Hello"}],
    )
except openai.APIStatusError as error:
    print(error.status_code)                           # 400
    print(error.type)                                  # invalid_request_error
    print(error.body["message"])                       # unknown model: no-such-model
    print(error.response.headers.get("x-request-id"))  # the id of this request

它返回状态码 400 和如下响应体:

400 JSON
{
  "error": {
    "type": "invalid_request_error",
    "message": "unknown model: no-such-model"
  }
}

错误响应体

有两种响应体。/v1/messages 以 Anthropic 格式作答;其他所有端点以 OpenAI 格式作答。两者的详细信息都在 error.type 和 error.message 中,因此一个解析器即可适用于所有端点。

401 OpenAI 格式
{
  "error": {
    "type": "authentication_error",
    "message": "Invalid API key",
    "code": "invalid_api_key",
    "param": null
  }
}
401 Anthropic 格式
{
  "type": "error",
  "error": {
    "type": "authentication_error",
    "message": "Invalid API key"
  }
}
字段 类型 说明
error.type string 错误的种类。见下文的错误类型。
error.message string 给人看的一句话。下表中的文本除其可变部分(显示为 <id>、<n> 或 …)外是稳定的。
error.code string | null 出现在部分 OpenAI 格式的错误中:401 对应 invalid_api_key,413 对应 request_too_large,对话过长时为 context_length_exceeded,其他情况为 null 或不存在。请先根据状态码和 error.type 判断,把 code 视为补充信息。
error.param null 与 code 出现在相同的错误中。始终为 null。
type string 仅限 Anthropic 格式。始终为 error。

错误类型

类型 状态码 说明
invalid_request_error 400, 413, 415, 422 请求本身有误:某个字段、请求体、模型 id,或模型不接受的输入。请修改请求后再重新发送。
authentication_error 401 API 密钥缺失、未知或已被撤销。
not_found_error 404 路径不存在。
rate_limit_error 429 余额不足以支付该请求、被限流保护拦下,或模型繁忙。消息会说明是哪一种。
api_error 500, 502, 503, 504 请求没有问题,故障出在我们这一侧。请重新发送。
server_error 500 请求没有问题,故障出在我们这一侧。请重新发送。

状态码

状态码 类型 消息 何时
400 invalid_request_error Failed to parse the request body as JSON: … 请求体不是有效的 JSON。
400 invalid_request_error unknown model: <id> model 不是已发布的 id。在 /v1/responses 上,缺少 model 时也会如此。
400 invalid_request_error No user message provided Shannon 档位:请求不包含用户文本。
400 invalid_request_error <id> does not accept image input 向不支持图片输入的托管开源权重模型发送了图片。
400 invalid_request_error audio and video input are not supported by any hosted model 向托管开源权重模型发送了音频或视频部分。
400 invalid_request_error <id> does not accept response_format 向不支持结构化输出的托管开源权重模型请求了结构化输出。
400 invalid_request_error <id> does not accept response_format json_schema; use json_object 向只接受 json_object 的托管开源权重模型发送了 JSON schema。
400 invalid_request_error unknown reasoning effort '<value>'; expected off, low, medium or high 推理强度不是所列的值之一。适用于托管开源权重模型。
400 invalid_request_error count_tokens is available for the hosted open models; unknown model: <id> 对托管开源权重模型之外的模型请求了 tokens 计数。
401 authentication_error Missing authentication 未发送 API 密钥。
401 authentication_error Invalid API key 密钥未知、已被更新的密钥替换,或已被撤销。
404 not_found_error Not Found 路径不存在,例如 /v1/embeddings。
405 api_error Method Not Allowed 路径存在,但不支持此 HTTP 方法。
413 invalid_request_error Failed to buffer the request body: … 请求体大于 32 MiB。
415 invalid_request_error Expected request with `Content-Type: application/json` Content-Type 请求头缺失,或不是 application/json。
422 invalid_request_error Failed to deserialize the JSON body into the target type: … 缺少必需字段(如 messages),或某个字段的 JSON 类型有误。
429 rate_limit_error Quota exceeded. Upgrade your plan at shannon-ai.com/plan 请求的输出上限大于你余额的剩余量。
429 rate_limit_error Too many requests. Retry in <n>s. 限流保护:你的账户在一分钟内发送了超过 120 个请求。<n> 是距这一分钟结束的秒数。
429 rate_limit_error Shannon Coder call quota reached. Upgrade your plan at shannon-ai.com/plan /v1/chat/completions 和 /v1/messages 上的 shannon-coder-1:你的计划在当前 4 小时窗口内的 Shannon Coder 调用次数已用完。
429 rate_limit_error Shannon routes are temporarily busy. Please retry. 模型此刻繁忙。
500 server_error The model backend failed to answer. Please retry. 模型未生成回复。
500 server_error An internal error occurred. Please retry. 准备请求时发生内部故障。
502 api_error The model backend failed to answer. Please retry. 模型未生成回复。适用于 Shannon 3 系列和托管开源权重模型。
503 api_error Could not verify your quota right now. Please retry. 此刻无法读取你的余额。
503 api_error token counting is temporarily unavailable for this model 此模型此刻无法进行 tokens 计数。
504 api_error upstream timed out 托管开源权重模型耗时超过了非流式请求的时间限制。

检查按以下顺序进行:API 密钥、请求体、模型 id、限流保护、余额。因此,密钥错误且请求体也错误的请求会返回 401。以 4xx 状态被拒绝的请求不收费。

余额与限流保护

每个请求一视同仁:没有速率档位,也没有单独的 API 配额。仍有两种情况会返回 429,类型均为 rate_limit_error。可通过消息区分。

余额

你的余额是当日计划额度(在 00:00 UTC 重置),以及之后的已购买信用额度。聊天和 API 使用同一份余额。

每个请求会先预留它的输出上限:max_tokens,在 /v1/responses 上是 max_output_tokens,两者都未发送时为 4,096。回复完成后,预留的数量会被请求实际用掉的数量取代。如果该数量超过剩余余额,请求会在模型启动之前返回 Quota exceeded。

同时运行的请求各自预留自己的数量。因此,许多 max_tokens 很高的并行请求,即使每个回复都很短,也可能在一瞬间占满余额。较低的 max_tokens 会少预留一些。

限流保护

限流保护允许每个账户每分钟 120 个请求。它按账户而非密钥计数,/v1/chat/completions、/v1/responses 和 /v1/messages 合并计算。这一分钟从你的第一个请求开始。

该分钟内的第 121 个请求会返回 Too many requests. Retry in <n>s.,且不收费。被拒绝的请求同样计数,因此不等待就重发的客户端会一直被拒绝,直到这一分钟结束。请按消息中的 <n> 秒等待。

并行发送的请求不会因为并行而被拒绝。它们会排队,依次得到答复。

GET /v1/models 和 tokens 计数端点不计入。

关于限制和余额的更多内容,以及在哪里查看你自己的余额和用量: 限制与余额 密钥与用量

哪些应该重试

只有当同一个请求可能成功时才重发。OpenAI 和 Anthropic SDK 本身已会对 429 和 5xx 回复自动重试两次;设置 max_retries(JavaScript 中为 maxRetries)可更改此行为。

错误 是否重发? 做法
400, 404, 405, 413, 415, 422 否。请先更正请求。 消息会指出有误的字段、模型或输入。
401 否。请使用有效的密钥。 在密钥与用量页面创建或替换密钥。
429 Too many requests 是,等待之后。 按消息中的秒数等待,然后发送相同的请求。
429 Quota exceeded 不能原样重发。 降低 max_tokens,等待 00:00 UTC 的每日重置,或充值。
429 Shannon Coder call quota reached 是,在下一个窗口。 窗口每 4 小时从 00:00 UTC 起算。换用其他模型可立即作答。
429 … temporarily busy 是。 等待几秒,之后每次再重试时等待更久。
500, 502, 503 是。 等待 1 秒,然后 2、4、8 秒。尝试几次后停止,并报告请求 id。
504 是。 将 stream 设为 true 后发送:时间限制只适用于非流式回复。

遵循此表的重试循环:

import re
import time

import openai
from openai import OpenAI

# max_retries=0: this loop does the retrying, the SDK does not add its own.
client = OpenAI(api_key="YOUR_API_KEY", base_url="https://api.shannon-ai.com/v1", max_retries=0)


def create_with_retry(attempts=5, **request):
    for attempt in range(attempts):
        try:
            return client.chat.completions.create(**request)
        except openai.RateLimitError as error:
            text = str(error)
            flood = re.search(r"Retry in (\d+)s", text)
            if flood:
                time.sleep(int(flood.group(1)))   # flood protection: wait out the minute
            elif "temporarily busy" in text:
                time.sleep(2 ** attempt)
            else:
                raise                             # balance: a wait of seconds does not help
        except (openai.InternalServerError, openai.APIConnectionError):
            time.sleep(2 ** attempt)              # 1, 2, 4, 8 seconds
    raise RuntimeError("the request failed after all attempts")


response = create_with_retry(
    model="shannon-3",
    messages=[{"role": "user", "content": "Hello"}],
)
print(response.choices[0].message.content)

流内的错误

流从第一个字节起状态码就是 200。之后的失败会以流内事件的形式到达:/v1/chat/completions 上是带 error 成员的数据块,/v1/messages 上是 error 事件,/v1/responses 上是 response.failed 或 error 事件。流的结束事件仍会随后到来。请将这样的回复视为失败,并像对待 5xx 状态一样重新发送请求。

比模型上下文窗口更长的对话会以这种方式报告,类型为 invalid_request_error,代码为 context_length_exceeded。重新发送之前,请缩短对话。

具体的帧在流式传输部分说明。 流式

请求 id

每个回复(包括错误)都有 x-request-id 请求头。它是你在自己的 x-request-id 请求头中发送的值;若未发送,则是 12 位十六进制字符。请记录它,并在就某个请求寻求帮助时一并提供。