# 错误码总览

> 业务码 = 0 表示成功；非 0 由各模块自定义。所有错误响应 HTTP 体形如：
> `{ "code": <业务码>, "message": "<人读消息>" }`

本页列出 Cloud WAF 平台**全部业务错误码**（与 `pkg/errors/codes.go` 真值源一一对账）。第三方接入方应该按 `code` 分支决策，不要解析 `message` 文本——文本随多语言/版本可能变化。

## HTTP 状态码 vs 业务错误码

平台两层错误模型：

| 层 | 字段 | 用途 |
|---|---|---|
| HTTP | status code | 协议层（4xx 客户端错、5xx 服务端错），适合负载均衡/网关层判断 |
| 业务 | `code` (json body) | 应用层细分错误，唯一可靠的"错在哪"信号 |

**约定**：
- 业务码 = 0 时 HTTP 必为 200。
- 业务码 ≠ 0 时 HTTP 一般 4xx/5xx，但少数幂等接口（如重复吊销 API Key）即使业务码非 0 也返回 200。
- HTTP 401（凭据失效）特意合并多种"未通过认证"成单一返回，避免侧信道暴露内部状态——业务层不再细分。
- HTTP 403（凭据有效但权限不足）会带具体业务码（1011 / 1052 / 1053 / 1056 等），客户端按业务码区分处置。

## 通用 HTTP 错误码（非业务码）

仅在请求未到达业务逻辑时返回（参数校验失败、协议错误、未授权）。

| code | HTTP | 含义 | 处置建议 |
|---|---|---|---|
| 400 | 400 | 请求格式错误（json 解析失败、必填字段缺失） | 修正请求体后重试 |
| 401 | 401 | 未认证（token 缺失/无效/过期） | 重新登录或更新 API Key |
| 403 | 403 | 权限不足（已认证但无对应 perm） | 申请权限或换账号 |
| 404 | 404 | 资源不存在 | 检查路径与 ID |
| 409 | 409 | 冲突（如重复创建） | 改名或先删除已有资源 |
| 500 | 500 | 服务器内部错误 | 重试一次；持续失败联系运维 |

## 模块码段分配

| 模块 | 范围 | 备注 |
|---|---|---|
| sys | 1000–1999 | 用户/角色/OEM/会话/API Key |
| guard | 2000–2999 | 域名/证书/策略/规则/IP 集 |
| node | 3000–3999 | 节点/节点组/IP/ACL 策略 |
| chart | 4000–4999 | 统计图表查询/导出 |
| alert | 5000–5999 | 告警任务/记录/联系人 |
| zdns | 6000–6999 | DNS 域名/记录 |
| notify | 7000–7999 | 通知通道/模板 |
| report | 8000–8999 | 报表任务/文件 |
| assistant | 9000–9999 | AI 助手/会话/知识库/MCP 工具 |
| geoip | 10000–10999 | GeoIP 查询 |
| channel | 11000–11999 | 渠道伙伴接入/onboard |

## sys 模块（1000–1999）

### 用户与认证（1001–1011）

| code | HTTP | 含义 | 处置建议 |
|---|---|---|---|
| 1001 | 404 | 用户不存在 | 检查用户名 / id |
| 1002 | 401 | 密码错误 | 重新输入；多次错误账号会被锁定 |
| 1003 | 403 | 用户被锁定 | 联系管理员解锁 |
| 1004 | 400 | 验证码错误或过期 | 刷新验证码重试 |
| 1005 | 401 | 会话已过期 | 重新登录 |
| 1006 | 409 | 用户名已存在 | 改名 |
| 1007 | 404 | 角色不存在 | 检查角色 id |
| 1008 | 409 | 角色名已存在 | 改名 |
| 1009 | 403 | 不允许删除该角色（系统内置/正在使用） | 先解绑后删 |
| 1010 | 400 | 旧密码错误（修改密码场景） | 重新输入旧密码 |
| 1011 | 403 | 权限不足 | 申请对应 perm |

### 设置与 OEM（1012, 1020–1023）

| code | HTTP | 含义 | 处置建议 |
|---|---|---|---|
| 1012 | 403 | 该设置不允许 API 修改（仅命令行/配置文件） | 走运维通道 |
| 1020 | 404 | OEM 不存在 | 检查 OEM id |
| 1021 | 409 | OEM 域名已存在 | 改 hostname |
| 1022 | 403 | 不允许删除默认 OEM | 该 OEM 受系统保护 |
| 1023 | 400 | Logo 文件非法（格式 / 尺寸 / 大小） | 重新上传符合规格的图片 |

### 订单与套餐（1030–1033, 1040）

| code | HTTP | 含义 | 处置建议 |
|---|---|---|---|
| 1030 | 404 | 订单不存在 | 检查订单 id |
| 1031 | 400 | 订单状态切换不合法 | 按状态机重试 |
| 1032 | 404 | 套餐不存在 | 检查套餐 id |
| 1033 | 409 | 套餐名已存在 | 改名 |
| 1040 | 404 | 公告不存在 | 检查公告 id |

### API Key 子模块（1050–1059）

| code | HTTP | 含义 | 是否可重试 | 是否需重新签发 |
|---|---|---|---|---|
| 1050 | 401 | API Key 无效/过期/吊销 | 否 | 是 |
| 1051 | 404 | API Key 不存在或不在可见范围 | 否 | N/A |
| 1052 | 400 | scope 超出当前用户 RBAC 边界 | 否 | 是（缩窄 scope 重签） |
| 1053 | 403 | API Key scope 不足，无法访问目标接口 | 否 | 视情况（可能需扩大 scope 重签，或换 user 通道） |
| 1054 | 400 | expires_in_days 超出上限 365 | 否 | N/A |
| 1055 | 400 | API Key name 必填 | 否 | N/A |
| 1056 | 403 | 跨 OEM/越权操作他人 API Key | 否 | N/A |

> **scope 匹配规则**：精确字符串相等，**不展开通配符**（写 `guard.*` 进 scope 不会匹配任何接口）。需要某模块全部权限时请逐条列叶子 perm key。

## guard 模块（2000–2999）

### 域名与策略（2001–2010）

| code | HTTP | 含义 | 处置建议 |
|---|---|---|---|
| 2001 | 404 | 域名不存在 | 检查 domain id |
| 2002 | 409 | 域名已存在 | 改名或先删除已有 |
| 2003 | 404 | 策略不存在 | 检查 policy id |
| 2004 | 409 | 策略名已存在 | 改名 |
| 2005 | 404 | 证书不存在 | 检查 cert id |
| 2006 | 409 | 证书名已存在 | 改名 |
| 2007 | 400 | 证书 PEM 格式非法 | 重新粘贴完整 PEM（含 BEGIN/END） |
| 2008 | 409 | 证书已绑定到该域名 | 跳过本次绑定 |
| 2009 | 403 | 策略正在被使用，不能删除 | 先解绑相关域名 |
| 2010 | 403 | 域名仍有活动节点，不能删除 | 先停用域名再删 |

### IP 集与规则（2011–2019）

| code | HTTP | 含义 | 处置建议 |
|---|---|---|---|
| 2011 | 404 | IP 集不存在 | 检查 set id |
| 2012 | 409 | IP 集名已存在 | 改名 |
| 2013 | 409 | IP 已在该集合中 | 跳过本次添加 |
| 2014 | 404 | WAF 规则组不存在 | 检查 group id |
| 2015 | 404 | 转发规则不存在 | 检查 forward id |
| 2016 | 404 | 调度组不存在 | 检查 schedule id |
| 2017 | 403 | 不允许删除默认调度 | 系统保护 |
| 2018 | 404 | CC 规则不存在 | 检查 rule id |
| 2019 | 404 | ACL 规则不存在 | 检查 rule id |

## node 模块（3000–3999）

| code | HTTP | 含义 | 处置建议 |
|---|---|---|---|
| 3001 | 404 | 节点不存在 | 检查 node id |
| 3002 | 409 | 节点名已存在 | 改名 |
| 3003 | 404 | IP 地址不存在 | 检查 ip id |
| 3004 | 409 | IP 地址已存在 | 跳过 |
| 3005 | 404 | 节点组不存在 | 检查 group id |
| 3006 | 409 | 节点组名已存在 | 改名 |
| 3007 | 404 | ACL 策略不存在 | 检查 policy id |
| 3008 | 409 | ACL 策略名已存在 | 改名 |
| 3009 | 404 | ACL 规则不存在 | 检查 rule id |
| 3010 | 403 | ACL 策略仍有规则，不能删除 | 先清空规则 |

## chart 模块（4000–4999）

| code | HTTP | 含义 | 处置建议 |
|---|---|---|---|
| 4001 | 500 | 图表查询失败（PG/ES 异常） | 重试一次；持续失败联系运维 |
| 4002 | 400 | 时间范围非法（stime > etime / 跨度过大） | 收窄时间范围 |
| 4003 | 400 | 字段非法（不在白名单 / 拼错） | 检查字段名拼写 |
| 4004 | 500 | 导出失败 | 减小数据量重试 |
| 4005 | 429 | 触发限流 | 退避后重试 |

## alert 模块（5000–5999）

| code | HTTP | 含义 | 处置建议 |
|---|---|---|---|
| 5001 | 404 | 告警任务不存在 | 检查 task id |
| 5002 | 409 | 告警任务名已存在 | 改名 |
| 5003 | 404 | 告警记录不存在 | 检查 record id |
| 5004 | 404 | 告警联系人不存在 | 检查 contact id |
| 5005 | 409 | 告警联系人已存在 | 跳过 |
| 5006 | 404 | 告警配置不存在 | 检查 config id |
| 5007 | 400 | 告警类型非法 | 用枚举内的合法值 |
| 5008 | 429 | 告警冷却中（防抖） | 等冷却结束 |
| 5009 | 429 | 告警限流（达到上限） | 调高上限或等周期重置 |
| 5010 | 500 | 告警开关切换失败 | 重试 |

## zdns 模块（6000–6999）

| code | HTTP | 含义 | 处置建议 |
|---|---|---|---|
| 6001 | 404 | DNS 域名不存在 | 检查 domain id |
| 6002 | 409 | DNS 域名已存在 | 改名 |
| 6003 | 404 | DNS 记录不存在 | 检查 record id |
| 6004 | 404 | DNS 操作不存在 | 检查 op id |
| 6005 | 400 | DNS 记录类型非法 | 用 A/AAAA/CNAME/MX/TXT 等 |

## notify 模块（7000–7999）

| code | HTTP | 含义 | 处置建议 |
|---|---|---|---|
| 7001 | 404 | 通知通道不存在 | 检查 channel id |
| 7002 | 409 | 通知通道名已存在 | 改名 |
| 7003 | 404 | 通知模板不存在 | 检查 template id |
| 7004 | 409 | 通知模板名已存在 | 改名 |
| 7005 | 500 | 通知发送失败 | 检查通道连通性后重试 |

## report 模块（8000–8999）

| code | HTTP | 含义 | 处置建议 |
|---|---|---|---|
| 8001 | 404 | 报表任务不存在 | 检查 task id |
| 8002 | 409 | 报表任务名已存在 | 改名 |
| 8003 | 404 | 报表文件不存在或已过期 | 重新生成 |
| 8004 | 404 | 报表记录不存在 | 检查 record id |
| 8005 | 500 | 报表生成失败 | 减小时间范围或导出字段后重试 |

## assistant 模块（9000–9999）

| code | HTTP | 含义 | 处置建议 |
|---|---|---|---|
| 9001 | 404 | AI 会话不存在 | 检查 session id |
| 9002 | 403 | AI 会话已归档 | 解档或新开会话 |
| 9003 | 500 | 消息操作失败 | 重试 |
| 9004 | 404 | MCP 工具不存在 | 检查 tool id |
| 9005 | 409 | 工具名已存在 | 改名 |
| 9006 | 404 | 知识库不存在 | 检查 kb id |
| 9007 | 404 | 文档不存在 | 检查 doc id |
| 9008 | 404 | AI 配置不存在 | 检查 config id |
| 9009 | 409 | 知识库名已存在 | 改名 |
| 9010 | 500 | 文档建索引失败 | 检查文档格式后重试 |

## geoip 模块（10000–10999）

| code | HTTP | 含义 | 处置建议 |
|---|---|---|---|
| 10001 | 500 | GeoIP 查询失败 | 重试一次 |
| 10002 | 404 | IP 不在 GeoIP 数据库内（私网/未收录） | 用公网 IP 查询 |

## channel 模块（11000–11999）

| code | HTTP | 含义 | 处置建议 |
|---|---|---|---|
| 11001 | 404 | Onboard 流程不存在 | 检查 onboard id |
| 11002 | 410 | 链接已过期 | 重新申请链接 |
| 11003 | 400 | 链接 token 非法 | 重新申请链接 |
| 11004 | 409 | 域名已存在 | 改名或先删除已有 |
| 11005 | 500 | 域名探测失败 | 检查域名 DNS 解析后重试 |
| 11006 | 400 | 证书非法 | 重新粘贴完整 PEM |
| 11007 | 400 | 域名尚未就绪，不能确认 | 等域名探测/证书安装完成 |

## 跨模块通用约定

- HTTP 401 → 凭据失效（统一码，故意合并避免侧信道）
- HTTP 403 → 凭据有效但权限/归属不匹配，业务码用于细分（1011 / 1052 / 1053 / 1056 / 1009 / 1022 / 2017 / 3010 等）
- HTTP 404 → 资源不存在或不在可见范围（典型业务码 1001 / 1020 / 2001 / 3001 …）
- HTTP 409 → 唯一性冲突（典型业务码 1006 / 1021 / 2002 / 3002 …）
- HTTP 429 → 限流，带 `Retry-After` 头（4005 / 5008 / 5009）
- HTTP 5xx → 服务端故障，建议指数退避重试（10ms → 100ms → 1s → 10s）

## 客户端处置建议

```python
# Python 伪代码：基于业务码而非 HTTP 状态决策
import requests
from time import sleep

resp = requests.get(api_url, headers={"Authorization": f"ApiKey {key}"})
body = resp.json()
code = body.get("code", -1)

if code == 0:
    return body["data"]
elif code == 1050:
    raise Exception("API Key 失效，请重新签发")
elif code == 1052:
    raise Exception(f"scope 越权：{body['message']}")
elif code in (4005, 5008, 5009):
    sleep(int(resp.headers.get("Retry-After", "60")))
    return retry()
elif code >= 5000 and resp.status_code >= 500:
    return exponential_backoff_retry()
else:
    raise Exception(f"业务错误 {code}: {body['message']}")
```

> 永远基于 `code` 字段决策，不要解析 `message` 文本——文本随多语言/版本变化。
