错误处理
每个 service 方法都返回一个普通的 error。传输层失败(DNS、连接被拒绝、
超时)会以 fmt.Errorf 包装后返回;任何被 Z.AI API 自身拒绝的请求会以
*client.APIError 形式返回,其中带有结构化字段,你可以据此分支处理,而不必
解析消息字符串。
resp, err := c.Chat().Create(ctx, req)
if err != nil {
var apiErr *client.APIError
if errors.As(err, &apiErr) {
fmt.Printf("[%d] %s\n", apiErr.Code, apiErr.UserMessage)
switch apiErr.Category {
case client.ErrorCategoryAuth:
// bad/expired key — don't retry, tell the user
case client.ErrorCategoryQuota:
// out of balance/quota — don't retry, surface to the user
case client.ErrorCategoryRateLimit:
if apiErr.IsRetriable {
// the client already retries this internally up to
// Config.MaxRetries — you'll only see it here if retries
// were exhausted or disabled (MaxRetries: -1)
}
}
return
}
// transport-level failure — network, DNS, timeout
}
APIError 字段:
| Field | Meaning |
|---|---|
HTTPStatus |
HTTP 状态码 |
Code |
Z.AI 的业务错误码(int) |
Message |
API 返回的原始消息 |
Category |
下列分类之一 |
UserMessage |
预先写好的、更友好的描述 |
IsRetriable |
client 自身的重试逻辑是否将其视为瞬时错误 |
RequestID |
用于支持/调试,当 API 返回时携带 |
辅助谓词:IsAuthError()、IsRateLimitError()、IsQuotaError()、
IsParameterError()、IsServerError()——等价于直接检查 .Category,提供
它们是为了在调用处更易读。
开箱即用的重试行为
Client.doRequest(被每个 service 使用)已经对 429/5xx/网络错误进行重试,
支持指数退避、抖动以及 Retry-After,最多重试到 Config.MaxRetries
(默认为 3;设为 -1 可禁用)。你通常不需要自己写重试循环——
APIError.IsRetriable 会告诉你某个到达你代码的错误是否已经耗尽了那些重试。
错误码参考
| Code | Constant | Category | Retriable |
|---|---|---|---|
| 1000 | ErrCodeAuthFailed |
Auth | 否 |
| 1001 | ErrCodeAuthNotFound |
Auth | 否 |
| 1003 | ErrCodeAuthTokenExpired |
Auth | 否 |
| 1005 | ErrCodeAuthNeed2FA |
Auth | 否 |
| 1113 | ErrCodeInsufficientBalance |
Quota | 否 |
| 1302 | ErrCodeRateLimitReached |
RateLimit | 是 |
| 1305 | ErrCodeServiceOverloaded |
Server | 是 |
| 1308 | ErrCodeUsageLimitReached |
Quota | 否 |
| 1309 | ErrCodeCodingPlanExpired |
Quota | 否 |
| 1310 | ErrCodeWeeklyMonthlyExhausted |
Quota | 否 |
| 1311 | ErrCodeModelNotIncluded |
Quota | 否 |
| 1313 | ErrCodeFairUsageViolation |
Quota | 否 |
| 1314 | ErrCodeEnterpriseExpired |
Quota | 否 |
| 1315 | ErrCodeEnterpriseKeyOnly |
Quota | 否 |
| 1316–1321 | usage-limit variants | Quota | 否 |
| 1210 | ErrCodeInvalidParameter |
Parameter | 否 |
| 1211 | ErrCodeUnknownModel |
Parameter | 否 |
| 1212 | ErrCodeMethodNotSupported |
Parameter | 否 |
| 1213 | ErrCodeParameterMissing |
Parameter | 否 |
| 1214 | ErrCodeParameterInvalid |
Parameter | 否 |
| 1215 | ErrCodeParametersConflict |
Parameter | 否 |
| 1221 | ErrCodeAPITakenOffline |
Parameter | 否 |
| 1222 | ErrCodeAPINotExist |
Parameter | 否 |
| 1261 | ErrCodePromptTooLong |
Parameter | 否 |
| 1301 | ErrCodeUnsafeContent |
Content | 否 |
| 1220 | ErrCodeNoPermission |
Permission | 否 |
| -1 | ErrCodeInternalError |
Server | 是 |
| 1200 | ErrCodeAPICallError |
Server | 是 |
| 1230 | ErrCodeProcessError |
Server | 是 |
| 1234 | ErrCodeNetworkError |
Server | 是 |
本 client 未识别的错误码默认归为 ErrorCategoryServer 且
IsRetriable: true——这是一个合理的默认值(把未知错误当作瞬时服务器问题
处理),但上表中列出的常量才是具有专门定制的 UserMessage 和重试决策的
那些。
事实来源:pkg/client/errors.go。
HTTP 200 内嵌失败 的怪异行为
少数端点(Agents 的 Invoke/AsyncResult)即使在业务层面调用失败时也会
返回 HTTP 200——失败信息内嵌在响应体中,而非通过 HTTP 状态码指示。从这些
方法收到非 nil 的 error 意味着传输层失败;要检测一个成功响应内部的
业务级失败,请检查 resp.Failed()(或直接查看 resp.Error/resp.Status)。
两种响应类型都在其 Failed() 方法上对此做了说明——如果你只检查
err != nil,很容易遗漏这一点。