错误码
错误响应统一格式:
{ "error": { "code": "VALIDATION_ERROR", "message": "display_name must be 2-24 characters", "i18n_key": "error.validation.display_name_length", "params": { "min": 2, "max": 24 } }}字段:
| 字段 | 类型 | 用途 |
|---|---|---|
code | string | 稳定机器枚举——代码分支判断的唯一字段 |
message | string | fallback 文案,仅显示用,不要 parse |
i18n_key | string | 客户端 i18n 翻译键 |
params | object | i18n 模板参数(如 {min:2,max:24}) |
下面按”高频 Top 15 错误码”+“按 HTTP 状态分类的全表”两部分组织。Top 15 每个独立 anchor,含 触发场景 / 根因 / 处理代码示例。
高频 Top 15 错误码深度解析
TOKEN_EXPIRED
| 维度 | 内容 |
|---|---|
| HTTP | 401 |
| Layer | 认证 |
| SDK 自动恢复 | ✓(自动调 /auth/refresh) |
触发场景:Access Token 5 分钟过期。SDK / curl 调用任意需要 user JWT 的端点都会遇到。
根因:Access Token TTL = 300s(短期 token + Refresh Token 长期 cookie 模型)。
处理:
try { await callApi();} catch (err) { if (err.code === "TOKEN_EXPIRED") { const { access_token } = await fetch("/auth/refresh", { method: "POST", credentials: "include", // refresh_token cookie 自动携带 }).then(r => r.json()); // 用新 token 重试 return callApiWith(access_token); } throw err;}SDK 已内置自动处理;只有你自己调 REST 时需要手写。详见 认证。
AGENT_TOKEN_INVALID
| 维度 | 内容 |
|---|---|
| HTTP | 401 |
| Layer | 认证 |
| SDK 自动恢复 | ✗(不可恢复,需手工干预) |
触发场景:Agent Token 被撤销 / 已过期 / 拼错。
根因:
- Token 旋转后旧 token 立即失效(紧急模式)或 7 天宽限后失效(常规模式)
- 用户在 Hashee app 主动撤销了该 Agent token
- 环境变量配错(如把 staging token 用到 prod URL)
处理:
agent.addErrorHandler((err) => { if (err.code === "AGENT_TOKEN_INVALID") { // 不可恢复 — 退出进程让 systemd / k8s 拉起新进程读新 token notifyOps("agent token invalid", { agent_id: AGENT_ID }); process.exit(1); }});详见 Token 旋转。
AGENT_KEYS_MISMATCH
| 维度 | 内容 |
|---|---|
| HTTP | 409 |
| Layer | E2EE |
| SDK 自动恢复 | ✗(不可恢复,需 ops 介入) |
触发场景:客户端本地持有的 X25519/Ed25519 私钥与后端注册的公钥不匹配。
根因:
- SDK 在
onKeyGenerated回调里生成了新私钥但未持久化 → 重启时 SDK 又 生成一对新私钥 → 与后端公钥不符 - 多个 Agent 进程共用同一
agent_id但不同私钥(违反”3 个 replica 共享 同一组私钥”约定) - 误删本地
keystore.json
处理:
# 1. 删除本地 keystore,让 SDK 下次启动时重新生成 + 重新注册rm -rf ~/.hashee/<agent_id>/keystore.json
# 2. 重启 Agent 进程systemctl restart my-agent注意:删除后对方公钥也变了——原对话方设备可能要触发”重新建立加密 通道”,这通常透明,偶尔需要重新发起。
详见 Agent 身份与密钥。
AGENT_LIMIT_EXCEEDED
| 维度 | 内容 |
|---|---|
| HTTP | 403 |
| Layer | 配额 |
| SDK 自动恢复 | ✗(业务侧调整) |
触发场景:用户已达 50 Agent 创建上限。
根因:V1 默认每用户 50 个 Agent(后端 env 可配)。详见 Agent 开发者平台规范 §配额。
处理:
用户视角: 1. 在 Hashee app 跟系统 Agent 说"列出我的 Agent" 看哪个不用了 2. 删掉不用的 → 名额释放 3. 重新创建
需要扩配额(V2 marketplace): → 联系 Hashee 开发者支持RATE_LIMITED
| 维度 | 内容 |
|---|---|
| HTTP | 429 |
| Layer | 限速 |
| SDK 自动恢复 | ✓(首次自动 backoff;多次后抛业务) |
触发场景:触发任意端点的限速。error.params.retry_after_ms
Retry-Afterheader 提供等待时长。
典型限速:
- Agent 消息发送:5 req/s/agent
- 文件上传:5 req/s/agent
- Capability Manifest 更新:1 req/min/agent
- Token 旋转:5 req/24h
处理:
async function sendWithBackoff(payload) { try { return await agent.send(convId, payload); } catch (err) { if (err.code === "RATE_LIMITED" || err.code === "MESSAGE_RATE_LIMITED") { await sleep(err.retry_after_ms ?? 1000); return agent.send(convId, payload); // 重试一次 } throw err; }}详见 速率限制与重试。
RELATION_REVOKED
| 维度 | 内容 |
|---|---|
| HTTP | 403 |
| Layer | 关系 |
| SDK 自动恢复 | ✗(业务清理 + 事件 handler) |
触发场景:用户主动撤销了和你 Agent 的 H2A 关系。Agent 尝试给该 user 发
消息会触发;也会收到 relation.revoked 事件。
根因:用户在 Hashee app 中”删除联系人 + 撤销 Agent 所有授权”。
处理:
agent.addEventHandler(async (event) => { if (event.type !== "relation.revoked") return; const { user_id, conversation_id } = event.payload;
// 1. 清理你存的对话明文 await db.deleteMessages({ conversation_id });
// 2. 清理用户档案 / 个性化数据 await db.deleteUserProfile({ user_id, agent_id: AGENT_ID });
// 3. 撤销外部资源 await externalApi.revokeUserAccess({ user_id });
// 4. 写本地审计日志 await audit.log({ action: "relation_revoked_handled", user_id });});详见 更新与撤销。
ARTIFACT_REVISION_CONFLICT
| 维度 | 内容 |
|---|---|
| HTTP | 409 |
| Layer | Artifact |
| SDK 自动恢复 | ✗(业务侧 reconcile + retry) |
触发场景:updateArtifact 调用的 based_on_revision 与后端当前
revision 不一致。
根因:并发 update 同一 artifact(如多 user 同时投票 + 多 replica 同时处理)。
处理:
async function updateWithReconcile(artifactId, patchFn) { for (let i = 0; i < 3; i++) { const latest = await agent.getArtifact(convId, artifactId); const next = patchFn(latest); try { return await agent.updateArtifact(convId, { ref_artifact: artifactId, based_on_revision: latest.revision, artifact: { payload: next }, }); } catch (err) { if (err.code !== "ARTIFACT_REVISION_CONFLICT") throw err; // 拉最新再 reconcile } } throw new Error("update conflict 3 retry exhausted");}WEBHOOK_SIGNATURE_INVALID
| 维度 | 内容 |
|---|---|
| HTTP | 401 |
| Layer | Webhook |
| SDK 自动恢复 | ✗(配置错) |
触发场景:webhook 收到的 HMAC-SHA256 签名不匹配 — SDK / 你自己验签时抛。
根因:
HASHEE_WEBHOOK_SECRET配错(staging vs prod 串号)- secret 已旋转但你的服务未更新
- MITM / 重放攻击(极少)
- body 在 reverse proxy 处被修改(如自动添加 trailing newline)
处理:
// 验签失败时 always 回 401(不让重试放大)if (!await verifyWebhookSignature(secret, headers, body)) { return new Response("invalid signature", { status: 401 });}详见 Webhook 协议。
WEBHOOK_TIMESTAMP_SKEW
| 维度 | 内容 |
|---|---|
| HTTP | 400 |
| Layer | Webhook |
| SDK 自动恢复 | ✗(服务器时间需校准) |
触发场景:X-Hashee-Timestamp 在 ±5 分钟容忍窗外。
根因:你的服务器时间漂移(VPS 无 NTP / Lambda 冷启动罕见 / Docker container 时钟错)。
处理:
# Linuxsudo timedatectl set-ntp truesudo systemctl restart systemd-timesyncd
# Docker# Dockerfile: 安装 chrony 或 ntp 自启动MANIFEST_TOO_LARGE
| 维度 | 内容 |
|---|---|
| HTTP | 413 |
| Layer | Capability Manifest |
| SDK 自动恢复 | ✗(精简 manifest) |
触发场景:Capability Manifest JSON 序列化后 > 128 KB hard cap。
根因:
- 工具数量过多(>50 tools)
input_schema嵌套过深description_i18n_key应该是 key 但写成了长文本
处理:
- 检查是否每个 tool 都需要完整 description(用 i18n key 而不是 inline 文本)
- 把相似工具合并(用 enum + oneOf 替代多个独立 tool)
- ≥ 64 KB 时后端 telemetry 已发 warning,及时响应
详见 Capability Manifest 规范 §2.3。
FILE_TOO_LARGE
| 维度 | 内容 |
|---|---|
| HTTP | 413 |
| Layer | 文件 |
| SDK 自动恢复 | ✗(拆分文件) |
触发场景:上传文件 > 100 MB hard cap。
根因:单文件硬上限 100 MB(V1)。
处理:
- 在你的 Agent 侧分块(chunk)
- 用 Artifact + R2 外链方式替代直接 upload
- 大量数据走数据库 / 外部 storage + 链接
WEBHOOK_PAYLOAD_TOO_LARGE
| 维度 | 内容 |
|---|---|
| HTTP | 413 |
| Layer | Webhook |
| SDK 自动恢复 | ✓(应自动走 R2) |
触发场景:webhook body > 1 MB hard cap。
根因:消息附带超大内容(不通过 R2 走)。
处理:通常你不该见到 — 后端会自动把 > 1 MB 的 payload 转为 R2 引用 发送 stub envelope。如果真见到,是后端 bug,请上报。
GRANT_REVOKED
| 维度 | 内容 |
|---|---|
| HTTP | 403 |
| Layer | Grant |
| SDK 自动恢复 | ✗(业务清理) |
触发场景:用户撤销了对应的数据访问 grant。
根因:用户在 Hashee app “Agent 详情 → 已授权数据” 里撤销 scope。
处理:
agent.addEventHandler(async (event) => { if (event.type !== "grant.revoked") return; const { grant_id, scope, user_id } = event.payload;
// 立即清理: // - 内存缓存 // - 已派生的衍生数据(向量化、指纹、AI 训练样本) await db.deleteGrantData({ grant_id });});详见 Data Grants。
AGENT_SUSPENDED / AGENT_BANNED
| 维度 | 内容 |
|---|---|
| HTTP | 403 |
| Layer | 治理 |
| SDK 自动恢复 | ✗(断开 + 不重连) |
触发场景:平台治理状态变更,Agent 被暂停或永久封禁。
根因:
AGENT_SUSPENDED(L2):内容违规 / 用户投诉AGENT_BANNED(L3):严重违规 / 重复违规
处理:SDK 自动断开;不重连。owner 应:
- 查
GET /agents/:id/governance看具体原因 - 联系平台申诉
DECRYPT_FAILURE
| 维度 | 内容 |
|---|---|
| HTTP | — |
| Layer | E2EE |
| SDK 自动恢复 | ✓(单条丢弃,不影响其他) |
触发场景:SDK 上报某条消息无法解密。
根因枚举:
| reason | 含义 | 排查方向 |
|---|---|---|
signature | Ed25519 验签失败 | 发送者用错私钥 / Agent 持有的发送者公钥版本旧 |
no_wrap | 找不到给本 Agent 的 wrap | 发送时还没拿到 Agent 公钥(首次连接竞态) |
unwrap | unwrap CEK 失败 | Agent 私钥不匹配 / 密钥误改 |
aead | AES-GCM tag 不匹配 | AAD 不一致(如时钟严重不同步影响 epoch) |
parse | 解密后不是合法 JSON | 协议版本不匹配(罕见) |
处理:
agent.addDecryptFailureHandler((report) => { metrics.increment("hashee.decrypt_failure", { reason: report.reason }); if (report.reason === "unwrap" || report.reason === "signature") { // 大概率密钥问题,报警人工介入 notifyOps("decrypt failure", report); }});详见 SDK 安全与加密边界。
全表参考
认证错误(4xx)
| 码 | HTTP | 说明 | 处理 |
|---|---|---|---|
UNAUTHORIZED | 401 | 缺 / 无效 token | 重新走登录 |
TOKEN_EXPIRED | 401 | access token 5 min 过期 | /auth/refresh |
AGENT_TOKEN_INVALID | 401 | Agent token 撤销 / 无效 | 重新生成 |
TURNSTILE_FAILED | 403 | Cloudflare Turnstile 未通过 | 让用户重新过验证 |
ACCOUNT_LOCKED | 423 | 5 次登录失败锁 15 min | 看 retry_after |
PASSKEY_VERIFICATION_FAILED | 401 | WebAuthn 签名验证失败 | 让用户重试 |
EMAIL_VERIFICATION_REQUIRED | 403 | 邮箱未验证 | 走 /auth/send-code |
权限错误(4xx)
| 码 | HTTP | 说明 | 处理 |
|---|---|---|---|
FORBIDDEN | 403 | 通用权限不足 | — |
KEY_ENUMERATION_BLOCKED | 403 | 没共享对话却查公钥 | 不要预查;走真实路由 |
ARTIFACT_NOT_FORWARDABLE | 403 | artifact 设了 forwardable: false | UI 隐藏转发按钮 |
AGENT_RESTRICTED | 403 | Agent L1 治理限制 | 检查 /agents/:id/governance |
AGENT_SUSPENDED | 403 | Agent L2 治理暂停 | SDK 自动断开 |
AGENT_BANNED | 403 | Agent L3 治理永久封禁 | SDK 不重连 |
NOT_CONVERSATION_MEMBER_UPLOAD | 403 | 非成员尝试上传 | — |
CONVERSATION_FORBIDDEN | 403 | Agent 不在 conv | — |
GRANT_DENIED | 403 | Grant 被拒 / 已撤销 | 业务 fallback |
GRANT_EXPIRED | 403 | Grant 时间窗到期 | 重新申请 |
GRANT_REVOKED | 403 | 用户撤销 grant | 业务清理 + 不再访问 |
RELATION_REVOKED | 403 | 用户撤销关系 | 业务清理 |
验证错误(4xx)
| 码 | HTTP | 说明 |
|---|---|---|
VALIDATION_ERROR | 400 | 字段验证未通过 |
FORWARD_TARGET_LIMIT | 400 | 转发超过 10 个目标 |
DEVICE_LIMIT_EXCEEDED | 400 | 超过每账户 5 设备 |
SHA256_MISMATCH | 400 | 文件上传密文 SHA-256 不匹配 |
MANIFEST_TOO_LARGE | 413 | Capability Manifest > 128 KB |
WEBHOOK_PAYLOAD_TOO_LARGE | 413 | webhook body > 1 MB |
FILE_TOO_LARGE | 413 | 文件 > 100 MB |
INVALID_ENVELOPE | 400 | wire envelope 解析失败 |
INVALID_SIGNATURE | 400 | Ed25519 签名验证失败 |
未找到错误(4xx)
| 码 | HTTP | 说明 |
|---|---|---|
NOT_FOUND | 404 | 通用资源不存在 |
USER_NOT_FOUND | 404 | user_id 不存在 |
GROUP_NOT_FOUND | 404 | group_id 不存在 |
CONVERSATION_NOT_FOUND | 404 | conv_id 不存在 |
MESSAGE_NOT_FOUND | 404 | message_id 不存在 |
AGENT_NOT_FOUND | 404 | agent_id 不存在 |
AGENT_NOT_REGISTERED | 404 | Agent 注册不完整 |
KEY_BACKUP_NOT_FOUND | 404 | 用户未上传备份 |
MIGRATION_SESSION_EXPIRED | 404 | 设备迁移信令 TTL 过期 |
INVITE_CODE_NOT_FOUND | 404 | 邀请码不存在或已用 |
配额与冲突(4xx)
| 码 | HTTP | 说明 |
|---|---|---|
AGENT_LIMIT_EXCEEDED | 403 | 超过用户 50 Agent 配额 |
GROUP_KEY_VERSION_CONFLICT | 409 | 群密钥并发轮换冲突 |
ARTIFACT_REVISION_CONFLICT | 409 | artifact_update revision 不匹配 |
MANIFEST_VERSION_CONFLICT | 409 | Capability Manifest 并发更新冲突 |
DEVICE_NAME_TAKEN | 409 | 设备名重复 |
GROUP_KEY_ROTATION_LIMIT | 429 | 6/h/conv 上限 |
速率限制(4xx)
| 码 | HTTP | 说明 | 处理 |
|---|---|---|---|
RATE_LIMITED | 429 | 通用速率限制 | 看 Retry-After |
MESSAGE_RATE_LIMITED | 429 | 消息发送 5 req/s/agent | 缓冲并节流 |
FILE_UPLOAD_RATE_LIMITED | 429 | 文件上传 5 req/s/agent | 串行化 |
MANIFEST_UPDATE_RATE_LIMITED | 429 | 1 req/min/agent | 合并改动 |
TOKEN_ROTATION_RATE_LIMITED | 429 | 5 req/24h | 不要循环 rotate |
超时与连接(4xx / 5xx)
| 码 | HTTP | 说明 |
|---|---|---|
DEVICE_AUTH_TIMEOUT | 408 | 设备授权 60s 超时 |
WEBHOOK_DELIVERY_TIMEOUT | 408 | Webhook 10s 内无响应 |
STREAM_TIMEOUT | 408 | 流式 5 min 上限 / 30s 无 delta |
Agent 加密相关
| 码 | 触发 | SDK 行为 |
|---|---|---|
AGENT_KEYS_MISMATCH | 客户端私钥与后端公钥不匹配 | throw;需 wipe local keystore |
AGENT_PUBKEY_NOT_FOUND | 后端没找到 Agent 公钥 | 极少见;联系支持 |
DECRYPT_FAILURE | SDK 上报某条消息无法解密 | 走 decryptFailureHandler |
Webhook 相关
| 码 | 触发 |
|---|---|
WEBHOOK_SIGNATURE_INVALID | HMAC 不匹配 |
WEBHOOK_TIMESTAMP_SKEW | timestamp ±5min 外 |
WEBHOOK_DELIVERY_DUPLICATE | delivery_id 24h 内已见过 |
WEBHOOK_PAYLOAD_INVALID | body 解析失败 |
WEBHOOK_AGENT_UNREACHABLE | 7 次重试失败后状态机 |
Stream 错误
| 码 | 说明 |
|---|---|
stream_timeout | 5 分钟无 done |
orphan_detected | 30 秒无 delta |
client_offline | 客户端长时间断开(V2) |
peer_revoked | 流式过程中关系被撤销 |
rate_limited | 流式限速触发 |
Stream 错误不自动重试——Agent 决定是否重新启动 stream session。
Tool Call denied.reason 枚举
artifact_response.payload.reason 在 status === "denied" 时给出:
| reason | 说明 |
|---|---|
user_rejected | 用户主动拒绝 |
user_timeout | high sensitivity 30s 超时 |
tool_not_supported_in_group | 群里 tool call 禁用 |
permission_revoked | 用户撤销了对应 permission_scope |
tool_timeout | 客户端执行超 tool.timeout_ms |
tool_not_implemented | 客户端不支持此 tool |
HTTP 状态码汇总
| 码 | 含义 |
|---|---|
| 200 | 成功 |
| 201 | 创建 |
| 204 | 成功无 body |
| 400 | 验证 / 参数错 |
| 401 | 认证失败 |
| 403 | 权限拒 |
| 404 | 未找到 |
| 408 | 超时 |
| 409 | 冲突 |
| 413 | Payload 过大 |
| 423 | 账户锁 |
| 429 | 速率限制 |
| 500 | 内部错 |
| 502 / 503 / 504 | 临时不可用,按 Retry-After 重试 |
错误处理代码模板
async function callApi<T>(fn: () => Promise<T>): Promise<T> { try { return await fn(); } catch (err: any) { switch (err.code) { case "TOKEN_EXPIRED": await refreshToken(); return fn(); case "RATE_LIMITED": case "MESSAGE_RATE_LIMITED": await sleep(err.retry_after_ms ?? 1000); return fn(); case "AGENT_KEYS_MISMATCH": notifyOps("agent keys mismatch", { agent_id: AGENT_ID }); throw err; case "GRANT_REVOKED": case "RELATION_REVOKED": await cleanupForUser(...); throw err; default: throw err; } }}相关页面
- API 概览
- 认证
- 速率限制与重试
- SDK 错误处理 — SDK 层封装
- Webhook 协议 — webhook 专属错误