错误码与频控
本文档列出了原生 API 可能返回的每一个错误码。示例中的金额为示意性占位符;实时价格请调用 GET /v1/prices。
统一错误响应结构
{ "error": { "code": 4001, "slug": "insufficient_funds",
"message": "Required 1300000 SUN, available 420000 SUN",
"field": null, "retryable": true,
"details": { "required_sun": 1300000, "available_sun": 420000 } },
"request_id": "req_01J9Z5P8T3WQ7X" }
| 字段 | 契约定义 |
|---|---|
code | 稳定整数错误码。永不重新编号,永不改变既有含义。新增错误场景将分配新编号。 |
slug | 与 code 一一对应的稳定下划线小写标识字符串。 |
message | 面向人类可读的英文描述信息,可能在不提前通知的情况下优化调整。仅供日志展示,请勿通过正则解析它。 |
field | 触发校验失败的具体请求字段名;非字段校验错误时为 null。 |
retryable | 布尔值,标识在稍后重试完全相同的请求是否有可能成功。 |
details | 可选的机器可读上下文字典;具体结构视具体错误码而定,下文中已单独说明。 |
request_id | 请求唯一标识,亦包含在 X-Request-Id 响应头中。联系支持团队时请附带此 ID。 |
在代码中请根据 slug 或 code 进行逻辑分支,切勿仅依赖 HTTP 状态码 —— 多个不同错误可能会共享同一个状态码(例如 409 既代表幂等性冲突,也代表无法赎回的订单,而两者的业务处理方式截然相反)。后续可能会引入新的错误码: 如果遇到未识别的错误码,请按 HTTP 状态码类别处理(4xx 代表客户端请求错误,切勿盲目无限重试;5xx 代表服务端临时错误,应使用退避算法重试)。
错误码号段分布:1000–1099 鉴权与凭据 · 1100–1199 频控与配额 · 2000–2099 请求校验 · 3000–3099 业务状态 · 4000–4099 资金账户 · 5000–5099 资源供给 · 9000–9099 内部异常。
错误代码列表
| 代码 | Slug | HTTP | 触发时机 | 是否重试 |
|---|---|---|---|---|
| 1001 | missing_credentials | 401 | 缺少 X-API-KEY、X-API-TIMESTAMP 或 X-API-SIGN 请求头。格式错误属于 1002;1001 代表完全未传递对应请求头。 | 否 —— 请检查客户端配置。 |
| 1002 | invalid_signature | 401 | X-API-SIGN 与服务端计算的签名不一致。 | 否。请仔细核对规范字符串构造;常见原因包括重新编码了 query、对缩进 JSON 签名却发送压缩 JSON,或者末尾包含换行符。 |
| 1003 | signature_timestamp_skew | 401 | X-API-TIMESTAMP 与服务器时钟相差超过 5 秒。details.server_time 返回服务器时间。 | 是,在校准服务器时钟后重试一次。请开启 NTP。 |
| 1004 | ip_not_allowed | 401 | 来源 IP 不在该密钥的 IP 白名单中。details.source_ip 返回识别到的来源 IP。 | 否。请在控制台中添加该 IP;位于 NAT 后面时请添加整个出口 IP 段。 |
| 1005 | insufficient_scope | 403 | 密钥有效,但未被授予此接口所需的权限范围。details.required_scope 指出缺失的权限名称。 | 否 —— 密钥权限由账户所有者显式设置,必须由人工在控制台勾选修改。 |
| 1006 | key_revoked | 401 | 该 API 密钥已被删除或已到期。 | 否。 |
| 1007 | key_inactive | 401 | 该 API 密钥存在但当前处于停用状态。 | 否。 |
| 1008 | account_suspended | 403 | 账户被冻结,仅允许读取,禁止下单。 | 否。请联系支持团队。 |
| 1009 | replayed_signature | 401 | 该签名已被使用过;在时钟偏移窗口内签名仅可使用一次。 | 否 —— 每次请求(包括重试)必须生成全新的时间戳与签名。 |
| 1010 | challenge_invalid | 401 | 登录/注册挑战验证失败:nonce 不存在、已过期、已使用,或签名无法恢复对应地址。 | 是,在重新请求 POST /v1/accounts/challenge 后重试一次。 |
| 1011 | bootstrap_token_expired | 401 | 15 分钟临时 Token 已过期,或被用于其无权访问的接口。 | 是 —— 重新执行钱包挑战与验证。 |
| 1012 | key_environment_mismatch | 401 | 密钥环境前缀与目标主机环境不匹配:例如将 ak_live_ 密钥发往 Nile 测试网。在查询密钥前即直接拦截。 | 否 —— 请使用对应环境的正确密钥。 |
| 1013 | session_expired | 401 | 控制台 Session Cookie 缺失、已过期或已登出。 | 否 —— 重新连接钱包登录。 |
| 1014 | csrf_token_invalid | 403 | 基于 Cookie 的写操作请求未携带与 CSRF Cookie 匹配的 X-CSRF-Token 请求头。 | 否 —— 读取 CSRF Cookie 并在写操作中携带。 |
| 1100 | rate_limited | 429 | 超出单 Key 或单 IP 的请求频控预算。响应头 Retry-After 指示需等待的秒数。 | 是 —— 遵循 Retry-After,配合随机抖动的指数退避算法重试。切勿死循环重试。 |
| 1101 | concurrency_limited | 429 | 当前账户处于处理中的订单或批处理任务过多。 | 是,待并发任务处理完毕后再发起。请降低调用并发度。 |
| 1102 | quota_exceeded | 429 | 达到账户日限额或月度总配额上限。details.resets_at 给出重置时间。 | 是,待到达 resets_at 后重试。 |
| 2000 | malformed_json | 400 | 请求体不是有效的 JSON,或 Content-Type 不是 application/json。 | 否。 |
| 2001 | validation_failed | 400 | 请求参数缺失、类型错误或取值超出范围。field 指出具体字段;details.constraint 给出校验规则。 | 否。 |
| 2002 | empty_patch | 422 | PATCH 请求体中未包含任何可修改的有效字段。 | 否。 |
| 2003 | tier_unavailable | 422 | 档位参数语法正确,但该资源目前不开放销售该时长。details.available_tiers 列出当前可用档位。 | 否。 |
| 2004 | quote_mismatch | 422 | 提交订单时既传入了 quote_id,又显式传入了与报价单相矛盾的订单参数。 | 否。 |
| 2005 | idempotency_key_required | 400 | 创建批量任务时既没有传 client_batch_id,也没有传 Idempotency-Key 请求头。 | 否。 |
| 2006 | duplicate_receiver | 400 | 同一批次任务中同一接收地址出现了多次。请合并金额。 | 否。 |
| 2007 | invalid_address | 400 | 非合法的 Base58Check TRON 地址(校验和错误、长度不对或传入了十六进制格式)。 | 否。 |
| 2008 | amount_out_of_range | 400 | 购买数量低于档位下限或高于上限。详见 details.min_amount / details.max_amount。 | 否。 |
| 2009 | batch_too_large | 400 | 批次地址数超过 100 个,或总能量超过单批次上限。 | 否。 |
| 2010 | invalid_webhook_url | 400 | Webhook 地址不是 HTTPS、解析为内网私有地址、包含凭据信息,或长度超过 2048 字符。 | 否。 |
| 2011 | unsupported_contract | 422 | 价格估算中的合约地址不是系统支持模拟的 TRC-20 代币合约。 | 否。 |
| 3001 | order_not_found | 404 | 订单不存在,或属于其他账户。出于隐私考虑两者不作区分。 | 否。 |
| 3002 | order_not_cancellable | 409 | 尝试取消已被系统锁定的批次子单。单个即时订单不支持撤单。 | 否 —— 检查订单状态,通常已交付完成。 |
| 3003 | receiver_is_ours | 422 | 接收地址为平台内部系统地址。 | 否。 |
| 3004 | receiver_not_activated | 422 | 接收地址未在链上激活,且下单时传了 activate: false。未扣除任何费用。 | 是,改为 activate: true 或先自行激活该地址后重试。 |
| 3005 | quote_expired | 409 | 报价单的 expires_at 已过期失效。 | 是 —— 先重新获取新报价单。 |
| 3006 | price_above_limit | 409 | 当前实时总价超过了您设定的 max_price_sun 上限。未扣除任何费用。 | 是 —— 等待价格回落,或提高上限值。 |
| 3007 | nothing_to_reclaim | 409 | 该订单从未产生过生效的链上委托,或者委托时长已自然到期回收。 | 否。 |
| 3008 | reclaim_unavailable | 409 | 该订单由第三方供应商成交,其外部资源不支持提前赎回回收。details.filled_by 说明成交来源。 | 否。 |
| 3009 | order_in_terminal_state | 409 | 对已处于终态的订单发起了状态变更请求。 | 否。 |
| 3010 | idempotency_conflict | 409 | 同一个幂等键被重复使用,但携带了不同的请求体。details.original_id 指向第一次请求的对象。 | 否 —— 客户端业务逻辑错误。请使用完全相同的内容重试,或更换全新幂等键。 |
| 3011 | subscription_exists | 409 | 该地址已经存在针对该资源的自动订阅配置。details.subscription_id。 | 否 —— 请调用 PATCH 修改现有订阅。 |
| 3012 | subscription_not_active | 409 | 对已取消的订阅发起了需要活跃状态的操作。 | 否。 |
| 3013 | webhook_limit_reached | 409 | 已注册满两个 Webhook 端点上限。 | 否 —— 请先删除或修改现有端点。 |
| 3014 | webhook_role_taken | 409 | 申请的端点角色已被占用。 | 否 —— 请调用 PATCH 交换端点角色。 |
| 3015 | request_in_progress | 409 | 携带该幂等键的相同请求正在后台执行中。Retry-After 给出建议查询时间。 | 请勿重试创建操作。 稍作等待后根据业务 ID 查询订单状态。 |
| 3017 | api_key_limit_reached | 409 | 账户已达到最大活跃 API 密钥数量上限。details.limit 与 details.used。 | 否 —— 注销废弃的密钥后即可腾出配额。 |
| 3018 | deposit_rotation_limited | 429 | 轮换充值地址过于频繁,超出频控上限。 | 是 —— 待 retry_after 后重试。 |
| 3019 | address_book_full | 409 | 地址簿已达 100 个条目上限。 | 否 —— 删除不需要的旧地址即可立即腾出空位。 |
| 4001 | insufficient_funds | 402 | 账户可用余额不足以支付订单总额及激活费。details.required_sun,details.available_sun。 | 是 —— 充值到账后,使用完全相同的 client_order_id 重试。 |
| 4002 | balance_reserved | 402 | 账面余额足够,但大部分已被处理中的并发订单预冻结。 | 是,待并发订单结算后重试。 |
| 4003 | currency_not_supported | 422 | 平台不支持使用该币种进行此项操作。 | 否。 |
| 4004 | ledger_conflict | 409 | 两个并发扣款请求发生账本竞争冲突,其中一个扣款失败。未扣除任何款项。 | 是,使用相同的幂等键立即重试。建议将单账户并发扣款操作进行串行化队列处理。 |
| 5001 | insufficient_supply | 503 | 无论自有流动性池还是外接供应商,均无法在合理的成本范围内满足该订单需求。未扣除任何款项。 | 是,稍后退避重试 —— 随着旧租单到期,能量会不断释放归还。对于短时订单,切换至更长档位通常能立即成交。 |
| 5002 | delegation_failed | 503 | 委托交易已构造但被 TRON 链拒绝,或长时间未能成功确认。预扣款已原路退回,订单终态为 failed 且带有 refunded_amount_sun。 | 是 —— 请更换全新的 client_order_id 重新下单。 |
| 5003 | chain_unavailable | 503 | 本地 TRON 全节点未响应,暂时无法验证或提交委托。 | 是,稍后退避重试。 |
| 5004 | provider_unavailable | 503 | 所有外部备用供应商均拒绝或超时,且自有库存不足。未扣费。 | 是,稍后退避重试。 |
| 5005 | receiver_capacity_exceeded | 422 | 接收地址无法再接收更多委托资源(TRON 限制单个地址的最大委托数量)。details.max_additional。 | 否 —— 需减少购买量或等待接收方现有委托到期。 |
| 5006 | supply_paused | 503 | 运营维护暂停销售该资源或该时长档位。 | 是,稍后重试;GET /v1/prices 中将不展示被暂停的档位。 |
| 5007 | capacity_unavailable | 503 | 自有容量不足,且根据合约策略不允许溢出至外部供应商。款项已退回。 | 是,稍后退避重试。 |
| 9000 | internal_error | 500 | 未捕获的服务端内部异常。单凭此响应无法确定订单最终是否已创建。 | 是 —— 必须使用相同的 client_order_id 进行重试,以确认第一次尝试是否生效。 |
| 9001 | timeout | 504 | 网关层超时;执行结果未知。 | 是,使用相同的 client_order_id 重试。 |
| 9002 | not_implemented | 501 | 该接口在规范中已定义,但当前部署尚未实现。 | 否。 |
参数校验错误(2000–2099)全部为 retryable: false;请根据错误信息修复请求参数,盲目重试必定返回相同错误。
供给保障机制: 下单时返回 503 是安全失败保证(fail-secure)—— 系统未做持久化且未扣款,因此使用相同的 client_order_id 重试完全安全。若发生已扣款但链上委托最终失败的情况(5002),资金已自动全额退还,订单以终态结束:请直接发起全新订单,不要重试旧单号。
重试策略规范
仅在返回 429、5xx 以及处理完原因后的 4001/4002/4004 时进行重试。切勿重试 400、401、403、404 以及非 4004 的 409 —— 这些错误需要修改请求内容或修复鉴权。创建订单时务必带上 client_order_id,以彻底避免重复购买风险。推荐使用起始延时为 250 毫秒、附带全量随机抖动(Full Jitter)的指数退避策略,单次最大延时上限设为 30 秒,总体超时建议控制在 60 秒左右 —— 超时后根据 client_order_id 查询订单实际结果。若响应中带有 Retry-After 请求头,请优先遵循该头指定的等待时长。
频控规则 (Rate limits)
频控规则基于单个 API 密钥实施,同时对单源 IP 设有底层兜底限制。每个 API 响应均携带标准响应头:RateLimit-Limit(额度)、RateLimit-Remaining(剩余次数)与 RateLimit-Reset(重置倒计时秒数)。超出限制时返回 HTTP 429、错误码 1100 rate_limited 以及 Retry-After 头。
默认限制(单个密钥,每秒请求数):
| 操作分组 | 频控阈值 |
|---|---|
订单创建 (POST /v1/orders, POST /v1/batches) | 30 rps |
数据读取 (GET 订单、报价、价格、资源) | 50 rps |
| 账户、API 密钥与 Webhook 管理 | 5 rps |