错误响应
每个失败的请求都会在顶层 error 键下返回相同的 JSON 信封,并附带一个 HTTP 状态来告知你粗略的类别。本页是线上契约;关于分支策略、重试和错误码目录的设计理念,请参阅错误。
代码示例
{
"error": {
"type": "validation_error",
"code": "E01001",
"name": "ValidationError",
"message": "Request validation failed.",
"doc_url": "https://bird.com/docs/api/errors/E01001",
"request_id": "req_01krdgeqcxet5s7t44vh8rt9mg",
"details": [
{ "param": "contact_id", "message": "this field is reserved and not yet supported" },
{ "param": "topic_id", "message": "this field is reserved and not yet supported" }
]
}
}信封字段
| 字段 | 是否始终存在 | 描述 |
|---|---|---|
| type | 是 | 用于粗略分支的大类:一个封闭枚举(auth_error、validation_error、rate_limit_error,……)。 |
| code | 是 | 与 E\d{5} 匹配的不透明、稳定标识符。唯一、不会重命名、不会重用;匹配时应以此为准。 |
| name | 是 | 人类可读的简称(ValidationError),用于提升日志可读性。始终与 code 成对出现,不可替代它。 |
| message | 是 | 人类可读的描述。不稳定;可以显示或记录到日志,切勿解析。 |
| doc_url | 是 | 指向该 code 文档页面的稳定链接。 |
| request_id | 是 | 关联 ID,也作为 X-Request-Id 响应请求头返回。在提交支持请求时请附上此值。 |
| param | 否 | 出错的字段,当错误由单个字段引起时出现。 |
| details | 否 | 以 {param, message} 对象表示的逐字段验证失败,其中 param 是类似 to[0].email 的点分路径。仅在 validation_error 响应中出现。 |
| vendor_code | 否 | 来自下游系统的原始代码(SMTP 回复码、支付拒绝码),在值得采取行动时出现。 |
HTTP 状态映射
每个 type 恰好映射到一个 HTTP 状态,因此状态码与信封始终一致。
| 状态 | type | 含义 |
|---|---|---|
| 400 | bad_request_error | 请求格式错误:无法解析的请求体或无效的请求头(例如错误的 Idempotency-Key)。 |
| 401 | auth_error | 请求携带了缺失、无效或已吊销的凭据。请参阅身份验证。 |
| 402 | billing_error | 请求需要组织尚未具备的支付方式、余额或套餐。请参阅账单与用量。 |
| 403 | permission_error | 凭据有效,但无权执行此请求。请参阅身份验证。 |
| 404 | not_found_error | 没有路由匹配此路径,或该资源在此工作区中不存在。请参阅区域。 |
| 409 | conflict_error | 请求与资源的当前状态冲突,包括幂等性冲突(E01004、E01005)。请参阅幂等性。 |
| 410 | gone_error | 资源曾经存在,但已被永久删除。 |
| 412 | precondition_error | 此请求的前置条件未满足。 |
| 413 | payload_too_large_error | 请求体超过了允许的最大大小。 |
| 421 | misdirected_error | 请求到达了一个无法为其提供服务的区域。请参阅区域。 |
| 422 | delivery_error | 消息已作为请求被接受,但无法按指定地址投递。 |
| 422 | validation_error | 请求体已成功解析,但一个或多个值无效。 |
| 425 | too_early_error | 请求到达的时间早于可处理的时间。 |
| 429 | rate_limit_error | 此工作区的某个速率限制组已耗尽。请参阅请求速率限制。 |
| 499 | client_closed_request_error | 连接在响应就绪前关闭,通常是因为调用方停止了等待。 |
| 500 | internal_error | 处理请求时我们这边出现了故障。请参阅幂等性。 |
| 501 | not_implemented_error | 该端点已在 API 中声明,但尚未实现。 |
| 503 | service_unavailable_error | 此请求所依赖的某个服务暂时不可用。 |
在 SDK 中处理错误
import { BirdRateLimitError, BirdValidationError, BirdAPIError } from "@messagebird/sdk";
try {
await bird.email.send({
from: { email: "onboarding@messagebird.dev", name: "Bird" },
to: ["delivered@messagebird.dev"],
subject: "Hello from Bird",
html: "<p>My first Bird email.</p>",
});
} catch (err) {
if (err instanceof BirdRateLimitError) console.log(`rate limited; retry in ${err.retryAfter}s`);
else if (err instanceof BirdValidationError) console.error(err.details);
else if (err instanceof BirdAPIError) console.error(err.code, err.requestId);
else throw err;
}from bird import APIStatusError, RateLimitError, ValidationError
try:
client.email.send(
from_={"email": "onboarding@messagebird.dev", "name": "Bird"},
to=["delivered@messagebird.dev"],
subject="Hello from Bird",
text="My first Bird email.",
)
except RateLimitError as err:
print("rate limited; retry after", err.retry_after)
except ValidationError as err:
print(err.status_code, err.details)
except APIStatusError as err:
print(err.status_code, err.code, err.request_id)if err != nil {
var rle *bird.RateLimitError
var ve *bird.ValidationError
var ae *bird.APIError
switch {
case errors.As(err, &rle):
fmt.Println("rate limited; retry after", rle.RetryAfter)
case errors.As(err, &ve):
for _, d := range ve.Details {
fmt.Printf("%s: %s\n", d.Param, d.Message)
}
case errors.As(err, &ae):
fmt.Printf("API error %s (status %d, request %s)\n", ae.Code, ae.StatusCode, ae.RequestID)
default:
log.Print(err) // transport: *bird.ConnectionError or *bird.TimeoutError
}
}try {
$bird->email->send(
from: 'Bird <onboarding@messagebird.dev>',
to: ['delivered@messagebird.dev'],
subject: 'Hello from Bird',
html: '<p>My first Bird email.</p>',
);
} catch (ApiException $e) {
// The server returned an error response. $status is the HTTP status, $type
// the coarse category, $errorCode the stable E##### code.
echo $e->status, ' ', $e->errorCode ?? $e->type ?? 'error';
} catch (ConnectionException $e) {
// All retry attempts failed, so no HTTP response is available.
echo 'transport error: ', $e->getMessage();
}错误码目录
公共 Bird API 返回的所有错误码,按代码范围拆分为每个范围一页。每个范围页面列出其错误码,每个错误码有自己的页面说明原因和处理方式。任何错误响应中的 doc_url 都直接链接到该错误码的页面。
| 范围 | 领域 | 错误码数 |
|---|---|---|
| E01xxx | 基础设施 | 30 个错误码,2 个已停用 |
| E02xxx | 认证与身份 | 9 个错误码,2 个已废弃 |
| E03xxx | 账单与套餐 | 12 个错误码 |
| E04xxx | 邮件发送与投递 | 67 个代码,3 个已废弃 |
| E05xxx | 域名与 DNS | 19 个错误码 |
| E06xxx | Webhooks | 7 个错误码 |
| E07xxx | 钱包 | 4 个错误码 |
| E10xxx | 配额 | 9 个错误码,2 个已停用 |
| E11xxx | IP 池与专用 IP | 4 个错误码,1 个已废弃 |
| E12xxx | SMS 发送与投递 | 49 个错误码,5 个已废弃 |
| E13xxx | Verify | 7 个错误码,1 个已废弃 |
| E14xxx | 号码 | 5 个错误码 |
| E15xxx | WhatsApp 发送与投递 | 49 个错误码 |
| E16xxx | 信任 | 1 个错误码 |
| E17xxx | 坐席邮箱 | 14 个错误码,2 个已废弃 |
| E19xxx | 注册合规 | 4 个错误码 |
| E21xxx | 语音与 SIP 中继 | 16 个代码,7 个已废弃 |
| E22xxx | 号码查询 | 4 个错误码 |
| E23xxx | 实时 | 1 个错误码 |
| E24xxx | Competitive Insights | 6 个错误码 |
| E25xxx | Sendability | 5 个错误码,1 个已废弃 |
| E27xxx | Inbox Insights | 5 个错误码 |
| E28xxx | Apple Messages for Business | 29 个错误码,2 个已废弃 |
| E32xxx | 操作确认 | 6 个错误码 |
相关内容
- 错误概念:分支策略、验证详情和 vendor_code 语义
- 身份验证:401 和 403 背后的凭据
- Idempotency-Key 请求头:409 冲突错误与安全重试
- 请求速率限制:策略、请求头,以及如何处理 429