先搞清楚:AI API错误分两类
所有AI API的错误码都可以归到两大类:
- 4xx 客户端错误 —— 你的请求有问题,需要改你的代码
- 5xx 服务端错误 —— 平台那边出问题了,你需要做容错处理
这个区分很重要,因为排查方向完全不同。4xx错误你改自己的代码就行,5xx错误你只能等平台修复或者切换到备用通道。
4xx客户端错误详解
401 认证失败
这是什么:你的API Key无效、过期或没有传。
常见原因:
- API Key复制的时候多了空格或少了字符
- Key已经被撤销或过期了
- 请求头格式不对,比如把
Authorization: Bearer sk-xxx写成了Authorization: sk-xxx - 用了A平台的Key去调B平台的接口(别笑,真有人这么干过)
排查步骤:
- 先确认API Key是否正确复制,注意前后有没有多余空格
- 到平台后台检查Key的状态,看是否被禁用
- 用curl或Postman单独测试一次,排除代码问题
- 检查请求头格式是否符合平台文档要求
预防措施:把API Key存在环境变量里,不要硬编码到代码中。定期检查Key的有效期,设置过期提醒。
error.code: "invalid_api_key";Claude返回 error.type: "authentication_error";DeepSeek返回 error_code: "invalid_api_key"。格式不同但意思一样,都是Key有问题。
429 速率限制(最常见的坑)
这是什么:你请求太快了,超出了平台的速率限制。
429是我遇到最多的错误,也是排查起来最花时间的。因为不同平台的限流策略差异很大。
| 平台 | 限流维度 | 免费层限制 | 付费层限制 |
|---|---|---|---|
| OpenAI | RPM + TPM | 3 RPM / 40000 TPM | 500 RPM / 200000 TPM |
| Claude | RPM | 5 RPM | 1000 RPM |
| DeepSeek | RPM + TPM | 3 RPM / 60000 TPM | 600 RPM / 300000 TPM |
| Gemini | RPM + RPD | 15 RPM / 1500 RPD | 300 RPM |
排查步骤:
- 查看响应头中的限流信息,大多数平台会返回
X-RateLimit-Remaining和X-RateLimit-Reset - 确认是RPM(每分钟请求数)超了还是TPM(每分钟token数)超了
- 检查代码里有没有循环调用没有加延迟
- 如果是并发场景,检查并发数是否超过了限制
解决方案:
- 实现指数退避重试(后面会给代码)
- 在请求前检查剩余额度,主动等待
- 用消息队列削峰填谷,控制并发
- 升级到更高的速率限制层级
Retry-After 头,告诉你多久之后才能重试。忽略这个值直接重试,只会让限流更严重,甚至可能导致账号被临时封禁。
400 参数错误
这是什么:你传的参数有问题,平台无法处理你的请求。
常见原因:
messages数组格式不对,比如缺少role字段max_tokens超出了模型的上限- 传了模型不支持的参数,比如给GPT-4o传了
frequency_penalty但值超出了范围 - 输入内容包含了模型不支持的特殊字符或过长的上下文
- 请求体JSON格式错误(少了逗号、多了逗号)
排查步骤:
- 仔细看错误响应里的
message字段,通常会告诉你哪个参数有问题 - 对照平台文档检查参数格式
- 用最小可用的请求体测试,逐步添加参数定位问题
408 请求超时
这是什么:你的请求发送太慢,平台等不及了。
这个错误比较少见,通常出现在网络不稳定或者请求体特别大的情况下。比如你传了一个10MB的图片做视觉理解,网络慢的时候容易触发。
解决方案:增加客户端的超时时间设置,优化网络连接,或者把大文件压缩后再传。
5xx服务端错误详解
500 内部服务器错误
这是什么:平台的服务器挂了。
这是最让人头疼的错误,因为你什么都做不了,只能等。但500错误也有不同的情况:
- 偶发500:偶尔出现一次,重试通常能成功。可能是服务器负载不均衡导致的
- 持续500:连续出现,说明平台在出事故。这时候应该切换到备用通道
- 特定模型500:只有某个模型报500,其他模型正常。可能是该模型在维护或更新
排查步骤:
- 先去平台的状态页看有没有故障公告(OpenAI、Claude、DeepSeek都有状态页)
- 换一个模型试试,看是不是特定模型的问题
- 检查你的请求是否触发了某些边界条件(比如极端长度的输入)
- 到社区或Twitter搜索,看其他开发者是否也在报同样的错
502 网关错误
这是什么:平台的网关/反向代理无法从上游服务获取响应。
502通常意味着平台的前端服务器正常,但后端的模型推理服务出了问题。可能的原因:
- 模型推理服务过载
- 模型服务正在重启或部署
- 网关配置错误(平台侧的问题)
处理方式:和500类似,重试+切换备用通道。502通常是暂时性的,几秒到几分钟后可能恢复。
503 服务不可用
这是什么:平台正在维护或者过载了,暂时无法处理请求。
503和500的区别在于,503通常是平台已知的状况,响应头里可能会带 Retry-After 告诉你多久后恢复。常见于:
- 平台计划内维护
- 突发流量导致服务过载
- 特定区域的节点故障
处理方式:读取 Retry-After 头,等待指定时间后重试。如果是过载导致的,降低请求频率。
主流平台错误码速查表
| 错误码 | OpenAI | Claude | DeepSeek | Gemini |
|---|---|---|---|---|
| 401 | invalid_api_key | authentication_error | invalid_api_key | API_KEY_INVALID |
| 429 | rate_limit_exceeded | rate_limit_error | rate_limit_exceeded | RESOURCE_EXHAUSTED |
| 400 | invalid_request_error | invalid_request_error | invalid_request_error | INVALID_ARGUMENT |
| 500 | server_error | api_error | server_error | INTERNAL |
| 502 | bad_gateway | overloaded_error | bad_gateway | UNAVAILABLE |
| 503 | service_unavailable | overloaded_error | service_unavailable | UNAVAILABLE |
注意各平台的错误码命名风格不一样。OpenAI用下划线命名(invalid_api_key),Claude也用下划线(authentication_error),Gemini用大写加下划线(API_KEY_INVALID)。写错误处理代码时要适配这些差异。
Python错误处理中间件
下面是我实际在用的错误处理中间件,支持自动重试、断路器和多通道故障转移:
import time
import logging
from typing import Optional, Callable
from dataclasses import dataclass
logger = logging.getLogger(__name__)
@dataclass
class APIResponse:
success: bool
data: Optional[dict] = None
error: Optional[str] = None
status_code: Optional[int] = None
retry_count: int = 0
class CircuitBreaker:
"""断路器:连续失败达到阈值后熔断,等待恢复"""
def __init__(self, failure_threshold: int = 5,
recovery_timeout: int = 60):
self.failure_threshold = failure_threshold
self.recovery_timeout = recovery_timeout
self.failure_count = 0
self.last_failure_time = 0
self.state = "closed" # closed | open | half_open
def record_success(self):
self.failure_count = 0
self.state = "closed"
def record_failure(self):
self.failure_count += 1
self.last_failure_time = time.time()
if self.failure_count >= self.failure_threshold:
self.state = "open"
logger.warning(
f"断路器打开,连续失败{self.failure_count}次"
)
def can_execute(self) -> bool:
if self.state == "closed":
return True
if self.state == "open":
if time.time() - self.last_failure_time > self.recovery_timeout:
self.state = "half_open"
return True
return False
return True # half_open 允许试探
class AIClient:
"""带重试和断路器的AI API客户端"""
def __init__(self, api_key: str, base_url: str,
max_retries: int = 3,
initial_backoff: float = 1.0):
self.api_key = api_key
self.base_url = base_url
self.max_retries = max_retries
self.initial_backoff = initial_backoff
self.circuit_breaker = CircuitBreaker()
def _make_request(self, payload: dict) -> dict:
"""发送HTTP请求(简化版,实际用httpx或requests)"""
import httpx
headers = {
"Authorization": f"Bearer {self.api_key}",
"Content-Type": "application/json"
}
resp = httpx.post(
f"{self.base_url}/v1/chat/completions",
headers=headers, json=payload, timeout=30
)
return resp
def call(self, payload: dict) -> APIResponse:
"""带重试逻辑的API调用"""
if not self.circuit_breaker.can_execute():
return APIResponse(
success=False,
error="断路器打开,服务暂时不可用",
status_code=503
)
for attempt in range(self.max_retries):
try:
resp = self._make_request(payload)
if resp.status_code == 200:
self.circuit_breaker.record_success()
return APIResponse(
success=True,
data=resp.json(),
retry_count=attempt
)
elif resp.status_code == 429:
# 指数退避重试
retry_after = resp.headers.get(
"Retry-After", None
)
if retry_after:
wait = float(retry_after)
else:
wait = self.initial_backoff * (2 ** attempt)
logger.warning(
f"429限流,等待{wait}秒后重试"
)
time.sleep(wait)
continue
elif resp.status_code == 500:
self.circuit_breaker.record_failure()
wait = self.initial_backoff * (2 ** attempt)
logger.warning(
f"500服务端错误,等待{wait}秒后重试"
)
time.sleep(wait)
continue
elif resp.status_code == 401:
self.circuit_breaker.record_failure()
return APIResponse(
success=False,
error="API Key无效,请检查配置",
status_code=401
)
else:
return APIResponse(
success=False,
error=f"未知错误: {resp.status_code}",
status_code=resp.status_code
)
except Exception as e:
wait = self.initial_backoff * (2 ** attempt)
logger.error(f"请求异常: {e},{wait}秒后重试")
time.sleep(wait)
continue
self.circuit_breaker.record_failure()
return APIResponse(
success=False,
error=f"重试{self.max_retries}次后仍然失败",
status_code=None,
retry_count=self.max_retries
)
# 多通道故障转移
class FailoverClient:
"""主通道失败时自动切换到备用通道"""
def __init__(self, clients: list[AIClient]):
self.clients = clients
def call(self, payload: dict) -> APIResponse:
for client in self.clients:
result = client.call(payload)
if result.success:
return result
logger.warning(
f"通道 {client.base_url} 失败: {result.error}"
)
return APIResponse(
success=False,
error="所有通道均失败"
)
# 使用示例
primary = AIClient("sk-xxx", "https://api.openai.com")
backup = AIClient("sk-yyy", "https://api.deepseek.com")
failover = FailoverClient([primary, backup])
result = failover.call({
"model": "gpt-4o-mini",
"messages": [{"role": "user", "content": "你好"}]
})
重试策略深入讲解
指数退避(Exponential Backoff)
指数退避是最基本也最实用的重试策略。核心思想很简单:每次重试的等待时间是上一次的2倍。
比如初始等待1秒,那重试序列就是:1s、2s、4s、8s... 这样可以避免在服务端压力大的时候雪崩式重试。
但光有指数退避还不够,实际生产中建议加上抖动(Jitter),在退避时间上加一个随机偏移量,防止多个客户端同时重试造成惊群效应。
import random
def calculate_backoff(attempt: int,
base: float = 1.0,
max_backoff: float = 60.0) -> float:
"""带抖动的指数退避"""
backoff = min(base * (2 ** attempt), max_backoff)
jitter = random.uniform(0, backoff * 0.25)
return backoff + jitter
断路器模式(Circuit Breaker)
断路器模式借鉴自电路中的保险丝。当连续失败次数达到阈值时,直接"断开"不再请求,避免浪费资源。过一段时间后进入"半开"状态,试探性地发一个请求,如果成功就恢复正常。
上面的代码里已经实现了断路器。关键参数:
failure_threshold:连续失败多少次后熔断(建议5次)recovery_timeout:熔断后多久进入半开状态(建议60秒)
多通道故障转移
这是最稳的方案。同时配置多个AI API服务商,主通道挂了自动切到备用通道。比如主用OpenAI,备用DeepSeek,再备用Claude。只要不是所有平台同时出问题,你的服务就不会中断。
成本方面,备用通道平时不产生费用(只有主通道失败时才会调用),性价比很高。你可以在 TokenNexus 上对比各平台的价格和稳定性评分,选择合适的备用通道。
API监控和告警最佳实践
光有错误处理还不够,你得知道错误什么时候发生、发生频率如何。以下是我推荐的监控方案:
1. 核心指标监控
- 请求成功率:低于99%就该告警了
- 平均响应时间:突然变慢可能是平台在出问题
- 429触发频率:频繁触发说明你的限流策略需要调整
- 各通道的失败率:帮助判断是否需要切换服务商
2. 告警策略
- 5xx错误连续出现3次:立即告警(电话/短信)
- 429错误1分钟内超过10次:普通告警(钉钉/飞书)
- 成功率低于95%持续5分钟:紧急告警
- 响应时间P99超过10秒:性能告警
3. 日志规范
每次API调用都应该记录:请求时间、平台、模型、状态码、响应时间、token消耗、重试次数。这些数据不仅能帮你排查问题,还能做成本分析。
import logging
import time
# 结构化日志格式
logging.basicConfig(
format='{"time":"%(asctime)s","level":"%(levelname)s",'
'"msg":"%(message)s"}',
level=logging.INFO
)
def log_api_call(platform: str, model: str,
status_code: int, latency: float,
tokens: int, retry_count: int = 0):
logging.info(
f"api_call platform={platform} model={model} "
f"status={status_code} latency={latency:.2f}s "
f"tokens={tokens} retries={retry_count}"
)
快速排查清单
最后整理一个快速排查清单,遇到报错时按顺序检查:
- 看状态码:4xx改你的代码,5xx做容错
- 看错误信息:平台返回的message字段通常告诉你具体原因
- 看响应头:检查
Retry-After、X-RateLimit-Remaining等信息 - 看平台状态页:确认是不是平台在出事故
- 最小化请求测试:用最简单的请求确认API本身是否可用
- 检查API Key:确认Key有效、额度充足、权限正确
- 检查网络:确认能正常访问API端点,DNS解析正常
写在最后:AI API报错不可怕,可怕的是没有预案。花半天时间实现错误处理中间件和多通道故障转移,能在关键时刻救你一命。如果你的项目已经在用单一API通道,建议今天就加上备用方案。需要找可靠的API服务商,可以到 TokenNexus 上对比各平台的稳定性和用户评价,选一个靠谱的备用通道。