响应头与错误码
AISIX 会通过多种面向调用方的 API 格式返回响应。失败的 Chat Completions 请求、Anthropic 风格 Messages 请求和透传路由请求使用的错误信封并不完全相同。
本参考帮助你理解响应头、重试提示、状态码和错误字段。排查时应先确认请求的 URL 路径,再查看对应错误格式,然后判断失败来自调用方、网关侧还是上游服务提供方。
错误响应格式
请根据请求的 URL 路径识别适用的错误信封。
| 响应来源 | 错误信封 | 阅读 |
|---|---|---|
OpenAI 兼容代理路由,例如 /v1/chat/completions、/v1/completions、/v1/embeddings、/v1/responses、音频、图像和 rerank | {"error": {...}} | OpenAI 风格代理错误 |
Anthropic 风格代理路由,例如 /v1/messages 和 /v1/messages/count_tokens | {"type":"error","error": {...}} | Anthropic 风格代理错误 |
/mcp 和 /mcp/{server} 下的 MCP 路由 | JSON-RPC 错误信封 | MCP 错误 |
/a2a/{agent} 下的 A2A 调用 | 上游 JSON-RPC 响应;转发前失败使用 HTTP 错误;上游分发失败使用 JSON-RPC 错误 | A2A 错误 |
/a2a/{agent}/.well-known/agent-card.json 下的 A2A Agent Card | 成功时使用 Agent Card JSON;网关或上游失败时使用 HTTP 错误响应 | A2A 错误 |
| 已配置的透传路由 | 转发上游状态码和响应体;AISIX 生成的失败使用 {"error": {...}} | 透传错误 |
代理响应头
运行时响应头会因端点而异,不应假设每个响应头都适用于所有 /v1/* 路由。
其中六个响应头是调试辅助信息,只有网关启动配置将 proxy.expose_debug_headers 设为 true 时才会发送:x-aisix-call-id、x-aisix-served-by、x-aisix-route、x-aisix-cache、x-aisix-cache-layer 和 x-aisix-cache-similarity。该设置默认关闭,因此调用方无从得知模型组中由哪个目标处理了请求、匹配了哪条语义路由,以及响应缓存如何应答。开启后,每个响应头仍只在下表所列条件下出现,且仅出现在 /v1/chat/completions 上;x-aisix-route 例外,语义路由器在 /v1/messages、/v1/messages/count_tokens 和 /v1/responses 上也会发送它。无论是否开启,访问日志和使用事件都会记录同样的路由与缓存信息。x-aisix-request-id、x-ratelimit-* 响应头和 Retry-After 不受该设置影响。
| 响应头 | 使用场景 |
|---|---|
x-aisix-call-id | 调试响应头,仅在 proxy.expose_debug_headers: true 时发送。出现在成功的 Chat Completions 响应中,值与 x-aisix-request-id 相同。关联请求请使用始终发送的 x-aisix-request-id。 |
x-aisix-request-id | 出现在每个代理响应中,用于将响应与该请求产生的任何访问日志和用量事件关联起来。一些 MCP 失败发生在用量核算之前;请参阅用量事件。该请求头默认在请求上同样被接受:发送你自己的 ID,AISIX 会全程使用该值而不再自行生成。从哪些请求头读取 ID 可配置,因此具体部署可以扩展或关闭该行为。参见复用自己的请求 ID。 |
x-aisix-served-by | 调试响应头,仅在 proxy.expose_debug_headers: true 时发送。出现在成功的 Chat Completions 路由响应中,用于识别实际处理请求的目标模型。访问日志在 served_by_model 中记录同一名称。 |
x-aisix-route | 调试响应头,仅在 proxy.expose_debug_headers: true 时发送。当语义路由器的某条路由处理了请求时,出现在成功的 /v1/chat/completions、/v1/messages、/v1/messages/count_tokens 和 /v1/responses 响应中,值为该路由的名称。访问日志在 semantic_route 中记录同一名称。 |
x-aisix-cache | 调试响应头,仅在 proxy.expose_debug_headers: true 时发送。出现在缓存策略覆盖的聊天请求上:hit、miss 或 bypass。只有 Cache-Control: no-cache 会产生 bypass;no-store 仍会执行缓存查询,因此报告 hit 或 miss。用于确认响应是否由网关缓存返回。 |
x-aisix-cache-layer | 调试响应头,仅在 proxy.expose_debug_headers: true 时发送。出现在缓存命中时:完全相同请求的匹配为 exact,向量相似度匹配为 semantic。用于区分命中来自哪个匹配层。 |
x-aisix-cache-similarity | 调试响应头,仅在 proxy.expose_debug_headers: true 时发送。出现在语义缓存命中时。匹配条目的余弦相似度,取值 0–1。用于校准策略的相似度阈值。 |
x-ratelimit-* | 按维度拆分的一组响应头,名称以 -requests、-tokens、-concurrent 结尾。当调用方 API Key 配置了限流时,会出现在成功的 Chat Completions 响应中,用于查看请求、Token 和并发限制状态。 |
X-RateLimit-LimitX-RateLimit-RemainingX-RateLimit-ResetX-RateLimit-Scope | 仅出现在 AISIX 自身限流器产生的 429 上,用于判断触发的是哪条限制以及何时可以重试。参见限流拒绝响应头。 |
Retry-After | 当网关能够给出重试提示时,会出现在限流、预算和所有候选不可用的拒绝响应中;当 AISIX 能解析上游 429 的重试提示时,也会透出该响应头。调用方可据此判断重试时间。 |
限流拒绝响应头
当 AISIX 自身的限流器拒绝一个请求时,返回的 429 会描述拒绝该请求的那一条限制,OpenAI 风格和 Anthropic 风格两种错误信封都是如此。
| 响应头 | 取值 |
|---|---|
X-RateLimit-Limit | 拒绝该请求的那条限制的配置上限。 |
X-RateLimit-Remaining | 该上限之下的剩余额度。触发拒绝时为 0。 |
X-RateLimit-Reset | 该限制重新放行请求所需的秒数。 |
Retry-After | 与上一行相同的秒数,客户端可以按自己已支持的那个响应头退避。 |
X-RateLimit-Scope | AISIX 扩展,标明是哪条限制拒绝了请求:rps、rpm、rph、rpd、tpm、tpd 或 concurrency。 |
X-RateLimit-Reset 是以秒为单位的时长,不是时间戳,因此客户端不需要与网关对时。对于固定窗口,它向下一个按 UTC 对齐的边界倒数:下一个整秒、整分或整点,日限制则倒数至 00:00 UTC。使用 window_type: sliding 时,它是请求可被放行前的精确等待时间,参见选择窗口类型。它永远不会为 0,最小值是 1。
一个调用方 API Key、一个模型或一条限流策略都可以同时配置多条限制。对于某一个请求,只有其中一条会拒绝它——AISIX 最先发现耗尽的那一条——响应头描述的就是这一条。X-RateLimit-Scope 让这些数字不再含糊,因为各维度的单位并不相同:x-ratelimit-limit: 1000 在 rpm 下是 1000 次请求,在 tpm 下是 1000 个 Token,在 concurrency 下是 1000 个在途请求。
有两种情况不适用上面的读法:
- 路由模型或语义路由。 分发会跳过每一个超出自身限制的目标并尝试下一个;当所有目标都用尽时,响应头描述的是最后一个拒绝的目标,那是配置在该目标上的限制,而不是配置在请求所寻址的别名上的。应当把它理解为"请求为什么无法被投递",而不是调用方自身的配额。
- 合议模型。 合议成员或评审模型超出自身限制时,请求会以
429失败,但该响应不携带这些响应头,也不携带Retry-After。
并发拒绝是唯一没有窗口的情况。并发槽位在某个在途请求结束时释放,而网关无法预测这个时刻,因此 X-RateLimit-Reset 和 Retry-After 都固定返回 60。
一个原始的拒绝响应如下:
HTTP/1.1 429 Too Many Requests
content-type: application/json
retry-after: 43
x-ratelimit-limit: 100
x-ratelimit-remaining: 0
x-ratelimit-reset: 43
x-ratelimit-scope: rpm
x-aisix-request-id: 018f3c1f-...
{"error":{"message":"request limit exceeded (requests)","type":"rate_limit_exceeded"}}
HTTP 响应头名称不区分大小写,AISIX 在网络上以小写形式写出。读取 X-RateLimit-Limit 和读取 x-ratelimit-limit 的客户端都能匹配到。
X-RateLimit-* 系列响应头只在 AISIX 自身拒绝请求时出现。以下情况不会出现:
- 成功响应。
200响应携带的是按维 度拆分的x-ratelimit-*系列:x-ratelimit-limit-requests、x-ratelimit-limit-tokens、x-ratelimit-limit-concurrent以及各自对应的remaining和reset。这一系列只报告调用方 API Key 自身的rpm、tpm和concurrency状态,不包含模型限制、限流策略,也不包含按秒、按小时、按天的窗口。在window_type: sliding下,reset的值表示当前计数全部移出窗口所需的时间。 - 上游返回的
429。 AISIX 能解析出服务提供方的Retry-After时会透传,但服务提供方的配额状态并非 AISIX 所知,因此不会给出自己的这几个值。 - 预算拒绝。 预算限制的是消费金额,而不是请求数或 Token 数。它同样返回
429,在错误体中带上结构化的预算字段,并携带Retry-After,其值为距预算周期重置的时间。
因此,X-RateLimit-* 是否出现,就是调用方判断"拒绝来自 AISIX 自身限流器"的依据。仅凭 Retry-After 无法判断——如上面两种情况所示,其他拒绝也会带上它。
代理状态码
当错误信封包含错误类型时,应优先查看它。状态码给出大类,错误类型通常能标识更精确的网关状态。
| 状态码 | 含义 |
|---|---|
400 | 请求无效。 |
401 | 调用方认证缺失或无效。 |
403 | 调用方已通过身份认证,但已配置的访问控制不允许该请求。 |
404 | 请求的资源未找到。网关生成的示例包括未知模型别名、MCP 服务器或 A2A Agent。上游服务也可能返回 404;AISIX 会保留上游 4xx 状态码。 |
413 | 请求体超过代理请求体大小限制。 |
422 | 内容被安全护栏拦截,可能是策略命中,也可能是关闭式失败的安全护栏无法评估该内容。参见安全护栏拒绝。 |
429 | 请求触发限流或预算拒绝。 |
501 | 解析出的服务提供方适配器未实现该端点。 |
502 | 上游服务提供方返回服务端失败,或适配器将上游失败映射为代理错误格式。 |
503 | 身份认证依赖项或服务提供方适配器不可用,所有路由候选都被运行时状态过滤,或严格预算执行无法核实预算。 |
504 | 上游请求超时。 |
OpenAI 风格代理错误
AISIX 的 OpenAI 兼容代理错误使用如下信封:
{
"error": {
"message": "...",
"type": "invalid_request_error"
}
}
当 AISIX 没有对应值时,会省略 param 和 code 字段。预算拒绝会在 error 对象中包含结构化预算字段,例如 scope、limit、spent、period 和 retry_after_seconds。limit 和 spent 使用组织币种。1.9.0 及更早版本的网关将它们命名为 limit_usd 和 spent_usd。
常见的 AISIX error.type 取值如下:
| 错误类型 | 常见状态码 | 含义 |
|---|---|---|
invalid_api_key | 401 | 调用方认证缺失或无效。无效、已过期或未映射的 JWT 也使用此类型;具体情况请检查 error.code。 |
permission_denied | 403 | 调用方 API Key 无权使用请求的模型、调用方客户端 IP 不在模型的 allowed_cidrs 范围内,或已验证的 JWT 不满足必需 Scope 或 Claim。 |
model_not_found | 404 | 请求的模型别名未配置。 |
invalid_request_error | 400 或 413 | 请求体或端点使用方式无效。过大的 OpenAI 风格请求会返回该错误类型和 413 状态码。 |
provider_unavailable | 503 | 选中的上游服务提供方适配器无法完成请求。 |
all_candidates_unavailable | 503 | 所有路由候选都被过滤或不可用。 |
api_error | 503 | AISIX 无法完成内部依赖操作,例如获取用于 JWT 身份认证的签名密钥,或完成语义路由器的路由决策;或者在严格预算执行下无法核实预算。error.code 会指明是哪项操作。 |
content_filter | 422 | 请求或响应被安全护栏拦截。安全护栏无法评估内容时 error.code 为 guardrail_unavailable;策略命中时该字段不存在。参见安全护栏拒绝。 |
billing_error | 429 | 适用于该请求的阻断型预算已达到限额。error.code 为 budget_exceeded。 |
rate_limit_exceeded | 429 | 请求超过已配置的限流规则。 |
not_implemented | 501 | 解析出的服务提供方适配器未实现该端点。 |
timeout | 504 | 上游请求超时。 |
upstream_error | 不固定,上游服务端失败通常为 502 | 上游服务提供方返回错误,AISIX 将其渲染为代理错误格式。 |
以上是调用方收到的取值。网关自己的记录对上游失败使用另一套名称:访问日志和用量事件的 error_class 都把上游 HTTP 错误记录为 upstream_status 而不是 upstream_error,因此请按各自的词汇匹配每种记录。参见读取错误类别。
身份认证错误码
OpenAI 风格代理错误可能包含以下用于调用方身份认证的稳定 error.code 值。Anthropic 风格代理错误除安全护栏拒绝外都会省略 code;请改用其 HTTP 状态码和按状态映射的 error.type。
这并不是 error.code 的全部取值。部分 OpenAI 风格路由会在安全护栏无法评估内容而拒绝请求时携带 guardrail_unavailable;具体接口差异请参见安全护栏拒绝。来源 IP 拒绝携带 ip_restricted,预算拒绝携带 budget_exceeded,无法核实预算时携带 budget_unverified(参见