错误与调试说明
说明 TRON API 的错误报告机制,介绍 HTTP 层错误、节点预校验错误和链上执行错误,并给出十六进制错误消息解码、请求重试策略和幂等性处理建议。
TRON API 错误需要先按发生阶段分类。不同阶段代表交易处于不同状态,重试策略也不同:
- HTTP 层错误:请求在到达 TRON 节点前,在网络传输或网关层发生的故障。
- 校验错误:节点在正式执行交易前,在预校验阶段拒绝了交易。广播响应通过
CONTRACT_VALIDATE_ERROR、SIGERROR、BANDWITH_ERROR等顶层错误码分类,并附带错误消息;消息的返回形式取决于visible。 - 链上执行错误:交易已成功通过校验并被打包进区块,但在链上实际执行时发生失败(如 TVM Revert、资源耗尽等)。这类错误的明细体现在交易回执中,而非接口广播的响应里。
注意生产环境必须区分上述三类错误,并分别处理。混淆错误阶段可能导致严重问题。例如,广播结果未知时,如果未先核对原
txID就创建替代交易,可能导致重复转账或重复的链上状态变更。
前置阅读
1. HTTP 层错误(网络与传输故障)
这类错误通常意味着请求没有完成一次完整往返,客户端没有拿到 TRON 节点返回的 JSON 业务响应。
| 错误症状 | 可能的原因 | 推荐处理策略 |
|---|---|---|
| 连接被拒绝(Connection Refused) | Endpoint 配置错误、目标节点不可用 | 核对请求 URL,或切换至备用节点 |
| 5xx 状态码 | 节点端发生过载、网络故障或内部服务器错误 | 读请求可按指数退避策略重试。写请求不要立即重发,应先确认节点是否已处理;错误持续出现时,再切换备用 Endpoint |
| 4xx 状态码 | 请求的 URL 路径错误、缺失必要的 HTTP Header、请求体 JSON 格式不合规范等 | 仔细检查并修复客户端的接口调用逻辑与参数构建方式 |
| 请求超时 (Timeout) | 节点负载过大响应缓慢、网络延迟严重或发起了开销过大的复杂查询 | 读请求可适当延长超时时间后重试。写请求超时后,无法确定节点是否已处理,应先查询链上或节点状态。单次查询的数据量较大时,可缩小查询范围,或改用相应的 TronGrid 扩展 API |
| 401 或 403 状态码 | 缺失或配置了错误的 API Key(仅在通过 TronGrid 接入时出现) | 参阅 TronGrid 速率限制 配置 API Key |
| 429 状态码 | 触发请求频次限制(仅在通过 TronGrid 接入时出现) | 采用退避重试,或检查套餐额度并申请调高上限 |
说明客户端没有收到业务响应,并不能证明节点尚未接收或处理请求。读请求通常可以重试;对于交易广播,应重播完全相同的已签名交易,以保持
txID不变。其他可能改变状态的写请求应先查询链上或节点状态,再决定是否重试。
2. 节点预校验错误
这类错误发生在节点已接收并解析请求之后、交易进入交易内存池(Mempool)之前。也就是说,交易未成功进入网络等待打包。
广播接口返回的失败响应通常如下所示:
{
"code": "CONTRACT_VALIDATE_ERROR",
"message": "636f6e7472616374207061726168657920646f6573206e6f74206d61746368"
}/wallet/broadcasttransaction 失败时通常会省略默认值为 false 的 result 字段。响应中的 code 表示失败类别,message 提供具体原因。未设置 visible 或将其设为 false 时,该字段通常采用十六进制编码;设置为 true 时,节点可能直接返回 UTF-8 明文。
常见顶层广播错误码
code | 含义 | 推荐处理方式 |
|---|---|---|
CONTRACT_VALIDATE_ERROR | 交易内容或当前链上状态未通过合约校验 | 解码 message,根据具体原因修正参数或账户状态 |
SIGERROR | 交易签名校验失败 | 检查私钥、签名算法,以及签名后是否修改了 raw_data |
BANDWITH_ERROR | 账户没有足够的 Bandwidth,或没有足够的 TRX 支付 Bandwidth、新账户创建、多签、Memo 等广播前费用 | 补充 Bandwidth 或 TRX,并结合交易是否创建账户、包含多签或 Memo 排查费用 |
BANDWITH_ERROR沿用协议枚举中的拼写,客户端必须按该原值匹配。
解码十六进制错误消息
收到十六进制形式的 message 时,可按以下方式解码;如果节点已经返回明文,则直接使用原值:
const message = "636f6e747261637420706172616d6574657220646f6573206e6f74206d61746368";
const decoded = /^(?:[0-9a-fA-F]{2})+$/.test(message)
? Buffer.from(message, "hex").toString("utf8")
: message;
console.log(decoded);
// 输出: "contract parameter does not match"
提示多数 SDK 会自动解码错误消息。业务直接处理 HTTP API 响应时,应先判断格式再决定是否解码,避免把节点返回的明文误当作十六进制数据。
常见的校验错误类型与解决方案
| 解码后的明文消息(示例) | 发生原因 | 推荐解决方案 |
|---|---|---|
account does not exist | 发起交易的签名方账户(或某些特定接口的接收方账户)尚未在链上激活 | 需向该地址转入任意小额 TRX 激活账户 |
balance is not sufficient | 账户余额不足以支撑该笔交易所声称的转移金额或资源消耗上限 | 及时充值 TRX |
signature error | 交易签名校验失败 | 常见于编码格式前后不一致(如 visible 标志混用,参见编码指南),或者在构建完交易后,客户端篡改了 raw_data 中的数据字段 |
Transaction expired | 本次提交未通过节点对 raw_data.expiration 的有效期校验,无法进入待处理池 | 先确认 expiration 是已经早于节点的下一可用出块时间,还是设置得距链头过远,再按下方业务重试策略处理;如果此前可能提交过,应先核对原 txID 的固化状态 |
Validate ... contract error, ... | 智能合约调用的特定入参校验失败 | 仔细查阅完整的错误消息,这通常由于函数参数的 ABI 编码错误或值不符合要求导致 |
frozenBalance must be positive | 质押金额设置为 0 或负数 | 传入合规的正整数(单位为 sun,即 10^-6 TRX) |
delegateBalance must be greater than or equal to 1 TRX | 资源代理的金额低于系统的最低限制 | 提高代理金额,使其大于或等于 1 TRX |
3. 链上执行阶段错误
智能合约交易即使通过节点预校验并被打包,仍可能在虚拟机(TVM)执行阶段失败。此类执行结果记录在交易收据(Transaction Receipt)中,可通过以下接口查询已固化的执行结果:
POST /walletsolidity/gettransactioninfobyid
Content-Type: application/json
{ "value": "<txid>" }查询返回后,应先确认顶层 result 未标记为 FAILED,再通过 receipt.result 判断 TVM 执行结果。receipt.result 的完整状态码枚举定义在底层的 protocol/src/main/protos/core/Tron.proto 文件中,详情如下:
receipt.result 字段值 | 枚举索引 | 具体执行含义 |
|---|---|---|
DEFAULT | 0 | 初始状态(已固化的交易中几乎不会出现此值) |
SUCCESS | 1 | 交易已成功执行 |
REVERT | 2 | 智能合约执行异常中断(触发了 REVERT 操作码,即 assert 或 require 条件校验失败) |
BAD_JUMP_DESTINATION | 3 | 合约字节码中存在无效的跳转指令 |
OUT_OF_MEMORY | 4 | 合约运行中内存分配超限 |
PRECOMPILED_CONTRACT | 5 | 预编译合约执行错误 |
STACK_TOO_SMALL | 6 | 虚拟机栈下溢错误(尝试从空栈弹出数据) |
STACK_TOO_LARGE | 7 | 虚拟机栈溢出(超出了 1024 层的最大栈限制) |
ILLEGAL_OPERATION | 8 | 合约指令集中包含非法或不被支持的操作码 |
STACK_OVERFLOW | 9 | 执行引擎栈空间溢出 |
OUT_OF_ENERGY | 10 | 交易在执行过程中耗尽了所配给的能量额度(Energy) |
OUT_OF_TIME | 11 | 实际执行超过单笔交易的 CPU 时间限制,或特定协议检查直接触发该结果;需结合具体错误消息判断 |
JVM_STACK_OVER_FLOW | 12 | JVM 级别栈溢出(常见于过深或失控的递归合约调用) |
UNKNOWN | 13 | 其他未被归类的异常或系统内部错误 |
TRANSFER_FAILED | 14 | 合约内部发起的 TRX 或 TRC-10 Token 转移操作失败 |
INVALID_CODE | 15 | 尝试部署或调用的智能合约字节码格式校验未通过 |
提示当
receipt.result返回REVERT时,可以进一步提取交易响应中contractResult[0]字段的数据。该数据遵循 Solidity 标准的Error(string)错误信息 ABI 编码格式,经解码后即可还原出合约抛出的具体文字原因(如"Insufficient allowance")。
业务重试策略
针对不同类别的错误,业务系统应当实施针对性的重试处理方案:
| 错误类别 | 原请求能否直接重试? | 推荐重试机制与解决步骤 |
|---|---|---|
| 网络异常或请求超时 | 视请求而定 | 读请求可退避重试。交易广播只重播完全相同的已签名交易;其他写请求先查询处理结果,避免重复操作。 |
| 5xx 服务端错误 | 视请求而定 | 处理方式与网络异常相同;若节点持续报错,可切换至备用 Endpoint,但仍需保持写请求的幂等策略。 |
| 429 频次限制 | 是 | 严格遵守 HTTP 响应头中的 Retry-After 时间后再发起重试,或申请提升 QPS 限制额度。 |
签名校验错误 (SIGERROR) | 不能直接重播带有无效签名的交易;如果 raw_data 仍有效,可以修正签名后重试 | 检查签名密钥是否匹配交易权限、签名格式是否正确,以及签名后是否修改了 raw_data。如果 raw_data 仍有效,保持其内容不变并重新收集有效签名;由于 txID 由 raw_data 计算,因此 txID 不变。如果 raw_data 已过期或必须修改,且此前可能成功提交过其他签名版本,应先按交易安全重播与替换流程核对原 txID,再重新构建交易。 |
交易过期 (Transaction expired) | 如果 expiration 已早于下一可用出块时间,不能;如果仅因设置得距链头过远,可以等待后重试 | 如果 expiration 已早于节点的下一可用出块时间,原交易不会重新变为有效,需要重新构建;如果这是首次且唯一一次提交,并已明确收到该错误,可以直接重建。如果 expiration 超出节点允许的约 24 小时未来范围,在 TAPOS 引用仍有效的前提下,可以等待链头推进后重播同一笔已签名交易。如果此前可能提交过或广播结果未知,创建替代交易前应先按交易安全重播与替换流程核对原 txID。 |
| 余额不足校验错误 | 可以,但原交易的 expiration 和 TAPOS 引用必须仍然有效 | 补足余额后,重播完全相同的已签名交易,无需重新构建。如果原节点因可选 RPC 交易缓存返回 DUP_TRANSACTION_ERROR,可改用其他可靠、已同步的节点重播,并继续跟踪原 txID。 |
链上执行失败(REVERT/OUT_OF_ENERGY/OUT_OF_TIME) | 否(必须修正原因后重新构建) | 这类交易已被链上打包但执行失败。应先根据回执和错误消息诊断原因:OUT_OF_ENERGY 可检查 fee_limit;OUT_OF_TIME 应根据具体错误消息判断是执行超时还是协议检查触发。修正后,再构建并广播一笔全新的交易。 |
交易幂等性设计
为 TRON 网络开发高并发支付或写入系统时,需要建立两项幂等与状态确认防线:
- 重复广播已签名的相同交易是安全的:TRON 全网通过
txID做唯一性去重。完全相同的已签名交易始终具有同一txID,不会导致交易执行两次;节点发现重复交易时可能返回DUP_TRANSACTION_ERROR。广播后如果因网络超时或服务端 5xx 未能确认结果,可以重播同一笔交易,但不要立即创建新的替代交易。 - 广播调用未报错不等于交易成功:
result: true只表示本次广播调用未返回错误。应保留原txID并查询已固化结果:普通 TRX 或 TRC-10 转账查询/walletsolidity/gettransactionbyid,智能合约交易还需查询/walletsolidity/gettransactioninfobyid。接口选择与返回数据的含义参见确认语义。
对于已经广播或广播结果未知的交易,不要直接创建替代交易。应按照交易安全重播与替换流程,使用能够查询原交易可能上链区间内所有已固化区块的数据源核对原 txID。只有在最新固化区块时间达到原交易的 raw_data.expiration,且所有相关区块中均未发现原交易后,才能重新构建。本地时间超过 raw_data.expiration、查询超时或 /walletsolidity/gettransactionbyid 单次返回空对象 {},都不能证明原交易未上链。
常用调试 Endpoint
| Endpoint 路径 | 主要调试用途 |
|---|---|
/wallet/triggerconstantcontract | 静态调用合约,无需广播即可进行模拟执行。常用于预估执行所需能量,或在正式发起交易前提前捕获可能发生的 TVM Revert。 |
/wallet/estimateenergy | 专用的能量估算接口(自 java-tron 4.7.0.1 起支持),较 triggerconstantcontract 能提供更精确的执行成本预测。 |
/walletsolidity/gettransactioninfobyid | 查询已固化交易的执行收据信息;判断交易本体是否存在时,应使用 /walletsolidity/gettransactionbyid。 |
/wallet/gettransactioninfobyid | 同样用于查询交易收据,但读取的是最新区块(链头)。数据生成较快,但在极少数情况下可能受微分叉或链重组影响。 |
/walletsolidity/getblockbynum | 通过区块高度抓取已固化的区块数据。常用于充值检测系统(Deposit Indexer)遍历、扫描和核算特定区块中的转账交易。 |
/wallet/getaccountresource | 查询特定账户当前的带宽与能量状态,便于估算账户可直接使用的资源。 |
/wallet/getchainparameters | 查询当前全网的动态链上参数。当资源价格或解锁等待期发生变化时,可在此查询 getUnfreezeDelayDays、getEnergyFee 等链上参数。 |
提示编写智能合约或开发 DApp 时,建议在正式提交交易前使用
/wallet/triggerconstantcontract模拟执行。该接口本身不要求传入交易级fee_limit;可将返回的energy_used按当前getEnergyFee换算后,与计划设置的fee_limit比较,从而提前发现预算过低的风险。
相关资源
- 交易签名与广播——API 工作流 —— 拆解交易构建、签名与广播各个环节潜在的出错点与生命周期
- 地址与数据编码 —— 详细了解编码规范以避免最易发生的格式校验错误
- 资源模型 —— 带宽、能量资源成本以及
fee_limit的设置方式 - 智能合约异常排查 —— 根据具体错误消息判断
OUT_OF_TIME是由执行超时还是协议检查触发 - 常见问题 FAQ —— 总结自 TRON 社区和生态开发者的经典疑难错误解答
Updated 8 days ago
