标准错误响应结构
{
"error": {
"message": "Rate limit exceeded for this token window.",
"type": "rate_limit_error",
"code": "rate_limit_exceeded"
}
}正在准备内容,请稍候。
正在切换页面...
网络较慢时会保留当前状态,直到新页面准备完成。
对接入方来说,最怕的不是报错,而是“报错了但不知道该不该重试、该看哪里、该修哪一层”。这页把错误结构、状态码、限流头和重试边界拆开说明。
建议优先读取 `error.message`、`error.type` 和 `error.code`。对生产系统来说,`status code + error.code` 的组合比单看提示文案更可靠。
{
"error": {
"message": "Rate limit exceeded for this token window.",
"type": "rate_limit_error",
"code": "rate_limit_exceeded"
}
}HTTP/1.1 429 Too Many Requests Content-Type: application/json Retry-After: 30 X-RateLimit-Limit-Minute: 60 X-RateLimit-Remaining-Minute: 0 X-RateLimit-Reset-Minute: 1712395230 X-RateLimit-Limit-Hour: 1000 X-RateLimit-Remaining-Hour: 824
状态码不是为了装饰响应,而是帮助客户端快速决定下一步动作:修请求、换令牌、等待、还是进入告警流程。
分钟和小时窗口的剩余额度、重置时间、`Retry-After` 都应该被客户端显式消费,而不是丢掉不用。
真正稳的客户端,不是“失败了就重试”,而是知道哪些错误该停下来修,哪些错误才应该等待后再试。
async function requestWithBackoff(payload, attempt = 0) {
const response = await fetch("https://api.ciyuan.com/v1/messages", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${process.env.TOKENAI_API_KEY}`,
},
body: JSON.stringify(payload),
});
if (response.ok) {
return response.json();
}
if (![429, 500, 502, 503, 504].includes(response.status) || attempt >= 3) {
throw new Error(`request failed: ${response.status}`);
}
const retryAfter = Number(response.headers.get("Retry-After") ?? 0);
const delayMs = retryAfter > 0 ? retryAfter * 1000 : 1000 * 2 ** attempt;
await new Promise((resolve) => setTimeout(resolve, delayMs));
return requestWithBackoff(payload, attempt + 1);
}