# 错误码与重试

区分模型 API 的 HTTP 错误和平台业务码，制定有限重试并提供可定位的报障信息。

## 先区分三类结果 [#先区分三类结果]

模型 API 通常使用 HTTP 状态和 OpenAI 或 Anthropic 风格 `error` 描述失败；平台控制台 API 通常使用 `code`、`message`、`data` 包装业务结果。它们不是同一套错误码。

异步媒体还存在第三类状态：HTTP 请求成功，但任务仍在 `PENDING`、`queued` 或运行中；之后可能返回任务 `FAILED` / `failed`。创建 HTTP 200 不能作为最终生成成功的证据。


**提示**

部分标准 Videos 处理分支使用平台业务错误包装，可能在 HTTP 200 中返回非零 `code`。稳健的 HTTP 客户端既要检查 HTTP 状态，也要识别响应中的业务码和任务终态；不能一律按 OpenAI SDK 异常判断成功。


## 错误响应格式 [#错误响应格式]

以下均为结构示意，文字与请求标识不是真实报错记录。OpenAI 风格错误：

```json
{
  "error": {
    "message": "Insufficient balance. Please recharge your account.",
    "type": "insufficient_quota",
    "code": "insufficient_balance",
    "request_id": "req_example",
    "retryable": false
  }
}
```

Anthropic 风格错误：

```json
{"type":"error","error":{"type":"invalid_request_error","message":"'max_tokens' is a required field"}}
```

平台业务错误示意：

```json
{"code":2001,"message":"参数错误","request_id":"req_example"}
```

| 字段 | 解释 |
| --- | --- |
| HTTP 状态 | 传输层 / 接口失败类别，SDK 通常据此抛出异常 |
| `error.type` | 错误分类，不等同于更具体的 `error.code` |
| `error.code` | 具体原因，例如 `insufficient_balance`；可能不存在 |
| `error.retryable` | 部分处理器提供的重试提示，仍需结合幂等性、额度和错误类别判断 |
| 顶层 `code` | 平台业务码；`0` 表示业务成功，非零应进入错误分支 |
| `request_id` | 可能位于顶层、`error` 内或响应头；有值时保存以便排障 |
| `data` | 平台成功数据或补充信息，可能省略，不应强依赖错误一定包含 `data` |

并非所有路径提供相同字段。比如 Messages 的处理器错误不保证 `request_id` 和 `retryable`，代理前置限流也可能采用更简的业务包装。解析未知字段应容错，但不能忽略已知失败信号。

## HTTP 状态与处理建议 [#http-状态与处理建议]

| HTTP | 常见原因 / 代码示例 | 建议 |
| --- | --- | --- |
| 400 | JSON 格式、必填参数、模型不支持的参数；`missing_model`、`invalid_image_url`、`embedding_not_supported` | 修改请求，不原样重复提交 |
| 401 | `missing_api_key`、`invalid_api_key`、`api_key_revoked` | 检查使用的是完整 API Key，不是登录 Token；不要反复重试 |
| 402 | `insufficient_balance`，OEM 场景也可能是平台额度不足 | 核对余额、额度与服务提供方，不靠等待解决 |
| 403 | `model_not_allowed`、`model_group_restricted`，Messages 也可能只给错误类型与文字 | 检查 Key 的模型限制与用户组权限 |
| 404 | 模型不存在 / 不可用，任务不存在 / 已过期；各端点代码可能不同 | 核对模型 ID、端点、任务 ID，不假设所有 404 都是模型错误 |
| 429 | 频率限制、RPM 限制或 Key 额度已耗尽 | 先区分短期限流与 `quota_exceeded`；仅对可恢复限流退避 |
| 500 | 服务或通道配置错误，如 `video_config_error` | 记录响应与请求标识；配置错误需服务方处理 |
| 502 / 503 / 504 | 上游、网关或计费依赖不可用 / 超时，具体格式可能不同 | 在请求可安全重试且总时限允许时有限重试 |

不要把 `type=insufficient_quota` 固定解释为 HTTP 402：它也可用于权限或额度错误。也不要把每个 429 都当成“稍等就好”；Key 的 元限额达到上限不会因短暂退避恢复。

## 平台业务码 [#平台业务码]

以下代码已在当前平台处理器中核对，但只是本页涉及的子集，不是全站统一穷举。控制台 API 可能以 HTTP 200 返回这些业务错误；认证中间件也可能直接返回非 2xx HTTP 状态。

| `code` | 当前处理器中的含义 |
| --- | --- |
| `0` | 业务成功，从 `data` 读取数据 |
| `2001` | 参数错误或参数不符合要求 |
| `2002` | 指定资源不存在，例如模型或用户 |
| `4004` | 资源不存在、不能操作或不可访问；看 `message`，不能仅凭此码推断资源类型 |
| `5000` | 内部处理失败，如获取列表、创建 Key 或查询失败 |
| `5001` | 获取完整 API Key 失败 |
| `5002` | 创建时未保存可恢复的完整 Key，无法恢复，应按提示重新创建 |

`5002` 不是 HTTP 5002；它是业务字段的值。平台 Token、Key 管理与计费接口的参数和响应不要照搬模型 API 的格式。

## 完整诊断请求示例 [#完整诊断请求示例]

下面请求 `GET /v1/models` 检查模型 API 的鉴权与基础 JSON 返回，不会提交生成任务。设置 `GFW_API_KEY`。cURL 同时显示状态和响应头；Python（依赖 `requests`）只对这个只读请求实施最多四次有界尝试，并识别业务错误。



**cURL**


```bash
curl --include --silent --show-error \
  --max-time 30 \
  https://api.gfw.net/v1/models \
  -H "Authorization: Bearer ${GFW_API_KEY:?请先设置 GFW_API_KEY}" \
  -H 'Accept: application/json'
```



**Python**


```python
import os
import random
import time
import requests

url = "https://api.gfw.net/v1/models"
headers = {"Authorization": f"Bearer {os.environ['GFW_API_KEY']}"}

with requests.Session() as session:
    for attempt in range(4):
        response = session.get(url, headers=headers, timeout=30)
        try:
            body = response.json()
        except ValueError as exc:
            response.raise_for_status()
            raise RuntimeError("响应不是 JSON，请核对 URL、反向代理与响应内容类型") from exc
        if not isinstance(body, dict):
            raise RuntimeError("响应不是预期的 JSON 对象")
        error = body.get("error", {})
        error_code = error.get("code") if isinstance(error, dict) else None
        quota_exhausted = error_code in {"quota_exceeded", "insufficient_balance"}
        can_retry = response.status_code in {429, 502, 503, 504} and not quota_exhausted
        if can_retry and attempt < 3:
            retry_after = response.headers.get("Retry-After", "")
            # 这里只解析秒数格式；过长等待直接交给调用方决定。
            if retry_after.isdigit():
                delay = float(retry_after)
                if delay > 30:
                    raise RuntimeError(f"Retry-After={retry_after}，请稍后再运行诊断")
            else:
                delay = min(8.0, 2.0 ** attempt) + random.uniform(0, 0.5)
            time.sleep(delay)
            continue
        response.raise_for_status()
        if isinstance(body, dict) and body.get("code", 0) != 0:
            raise RuntimeError(f"业务错误：{body['code']} {body.get('message')}")
        if error:
            raise RuntimeError(f"API 错误：{error}")
        request_id = response.headers.get("X-Request-ID") or body.get("request_id")
        print("HTTP:", response.status_code, "request_id:", request_id)
        print("model_count:", len(body.get("data", [])))
        break
```




网络异常在本例直接上抛，避免用重试掩盖 DNS、代理或 TLS 配置问题。读取模型列表成功，只证明该请求与鉴权成功，不证明某个生成模型通道也能执行。

## 限流与安全重试 [#限流与安全重试]

网关路由包含用户、Key 与 IP 层的限流，Key 配置还可包含每秒、RPM、TPM 和 元额度限制。运行配置、账号权限和上游限额可能不同，不应把某个默认数字当作账号的永久承诺。流式输出不会绕过限流，也不会让超限请求自动成功。

短期限流时优先遵守 `Retry-After`（如果提供），否则使用带随机抖动的指数退避，限制尝试次数与总等待时间。应用还应限制并发并排队，防止多个进程同时重试形成新的流量峰值。

对生成请求谨慎重试：连接超时可能发生在服务已受理之后。不要假设网关提供通用幂等键或自动去重；拿到异步任务 ID 后，只查询已有任务，不重复创建。工具操作需由应用实现幂等性；余额不足、参数错误、权限错误和配置错误都不应无差别自动重试。

流式开始后，错误可能出现在事件内部或表现为连接中断，HTTP 200 不再能说明最终成功。参阅 [流式输出](/docs/streaming)。

## 排障材料与相关文档 [#排障材料与相关文档]

提交问题时提供：脱敏后的端点和模型 ID、请求时间及其时区、HTTP 状态、错误类型与代码、请求 / 任务 ID、是否流式、是否已重试，以及 SDK 或客户端名称。保留最小可复现参数，不发送 API Key、登录 Token 或包含敏感内容的完整请求。

先确认 URL 没有重复 `/v1`，模型 ID 没有厂商路径前缀，接口方法正确。404 HTML 页面通常应优先核对地址或代理配置；JSON 模型错误应核对目录和授权；已受理的媒体任务按任务状态排查。

相关文档：[API Key 与鉴权](/docs/authentication)、[模型与能力](/docs/models)、[图片生成](/docs/image-generation)、[视频生成](/docs/video-generation)、[用量与调用日志](/docs/usage-logs)、[计费与充值](/docs/billing)。
