广播与 RPC 错误诊断
TRON 节点如何反馈交易广播失败和 RPC 限频。涵盖完整的广播响应码对照表、SERVER_BUSY 缓存交易数量超限处理、TronGrid 503 限流响应,以及广播后查询不到交易时的安全重试流程。
前置阅读
本文归纳客户端向 TRON 节点广播交易时可能遇到的报错类型,以及托管 RPC 网关返回限频响应时的处理对策。
注意交易被打包进区块之后发生的异常(如 TVM Revert、OutOfEnergy 资源耗尽、OutOfTime 执行超时等链上执行失败),见智能合约错误说明。报错整体分类与 Hex 错误消息解码细节,见错误与调试说明。
交易广播接口响应码
客户端调用 /wallet/broadcasttransaction 失败时,响应体返回顶层 code 字段。节点可能返回的完整响应码对照如下:
| 广播响应码 | 具体失败原因 | 推荐排查与修复方法 |
|---|---|---|
SIGERROR | 交易签名验证未通过 | 检查用于签名的私钥是否与发送方的 TRON 地址相匹配,并核对私钥的十六进制字符格式是否正确。 |
BANDWITH_ERROR | 广播前 Bandwidth 不足,或 TRX 余额不足以支付 Bandwidth、新账户创建、多签、Memo 等费用;不表示 TVM 执行阶段 Energy 不足 | 查询 /wallet/getaccountresource 和 /wallet/getchainparameters,结合交易是否创建账户、包含多签或 Memo 判断具体费用,并补充 Bandwidth 或 TRX。 |
DUP_TRANSACTION_ERROR | 相同 txID 通常已进入当前节点的 Pending Pool 或区块。若节点启用了 node.rpc.trxCacheEnable,也可能只是广播缓存命中 | 该错误不能证明交易已上链。有效期内可向原节点或其他可靠、已同步的节点重播完全相同的已签名交易;这笔交易的 txID 不变,也不会产生第二次执行。需要创建替代交易时,先按下文流程核对原 txID。 |
TAPOS_ERROR | 交易引用的 TAPOS 区块号与哈希无法在当前节点的近期区块记录中匹配 | 只有确认这是原交易的首次且唯一一次提交,且此前不存在其他提交或结果未知的尝试时,才可使用当前同步节点认可的近期区块重新构造并签名;最新固化块通常更稳妥。否则应先按下文安全重播与替换流程,通过固化时间和完整历史核对原 txID,确认原交易未上链后再重建。 |
TOO_BIG_TRANSACTION_ERROR | 交易数据的字节大小超出了网络单笔上限 | 常见于备注(Memo)字段过长。请缩减备注信息或拆分交易以缩减单笔字节大小。 |
TRANSACTION_EXPIRATION_ERROR | raw_data.expiration 未通过节点的有效期校验:已经早于下一可用出块时间,或晚于链头约 24 小时。节点构建交易时默认使用约 60 秒的有效期 | 尽量缩短构建、本地签名与广播之间的延迟,并确保 expiration 位于协议允许的范围内。自建节点可调整 trx.expiration.timeInMilliseconds;本地构建时也可设置有效的过期时间。修改 raw_data 会改变 txID,必须重新签名。若同一交易此前可能已经提交,应先按下文安全重播与替换流程核对原 txID。 |
SERVER_BUSY | 当前节点缓存的交易数量超过 node.maxTransactionPendingSize 设置的上限 | 请参阅下方 SERVER_BUSY 异常处理。 |
NOT_ENOUGH_EFFECTIVE_CONNECTION | 节点的有效 P2P 连接数低于 minEffectiveConnection,或交易未能广播给任何邻居节点 | 该结果可能发生在原交易已进入本地 Pending Pool、但未能向邻居传播之后。保留原 txID,只向其他可靠、已同步的节点重播完全相同的已签名交易,不要重建。自建节点还应检查 P2P 连接与同步状态。 |
OTHER_ERROR | 系统未分类的未知异常 | 保留并查询原 txID,同时查阅全节点后台日志获取详细异常堆栈。在原交易结果未明前,有效期内只重播完全相同的已签名交易,不要创建替代交易。 |
NO_CONNECTION | 该节点未建立起足够的 P2P 网络连接 | 请检查配置文件中的 seed.node 种子节点列表,并尝试重启节点。 |
CONTRACT_EXE_ERROR | 合约在静态预执行阶段因运行时异常或非法 Protobuf 结构而执行失败 | 检查合约调用的入参格式和 ABI 编码,并查阅节点的运行日志。 |
BLOCK_UNSOLIDIFIED | 启用 node.unsolidifiedBlockCheck 时,节点未固化区块数量达到或超过 node.maxUnsolidifiedBlocks 设置的阈值 | 等待节点恢复,并检查同步和 P2P 状态。如确需调整,可在评估风险后适当调大 node.maxUnsolidifiedBlocks,或通过 node.unsolidifiedBlockCheck 关闭该项检查;修改后需重启 FullNode。 |
CONTRACT_VALIDATE_ERROR | 预执行阶段的静态校验未通过(系统合约层校验) | 具体原因存放在 message 字段中。未设置 visible 或将其设为 false 时通常为十六进制;设置为 true 时可能直接返回 UTF-8 明文。请先判断格式再决定是否解码。参见下方 CONTRACT_VALIDATE_ERROR 详细解析。 |
CONTRACT_VALIDATE_ERROR 详细解析
CONTRACT_VALIDATE_ERROR 详细解析该错误码是一个通用的静态校验失败标识,真正的底层业务原因通常存放在 message 字段中。仅当返回值是偶数位十六进制字符串时才需要解码。常见的明文含义如下:
| 解码后的明文消息 | 原因说明 | 解决方案 |
|---|---|---|
account does not exist | 签名发送方或接收方的账户在链上不存在(从未被激活过)。 | 需要向该账户转入任意小额 TRX(如 1 sun)以在链上激活创建该账户。 |
Validate ... error, no OwnerAccount | 请求中传入的 owner_address(发送方地址)格式非法或不存在。 | 检查地址编码格式是否正确(如 visible 参数配置,参见编码指南)。 |
No contract or not a valid smart contract | 目标合约地址错误,或者该地址在链上并不是一个智能合约账户。 | 仔细核对部署的合约地址是否正确,避免指向普通外部账户(EOA)。 |
this node does not support constant | 当前连接的节点不支持只读合约常量调用(Constant Call)。 | 常见于自托管全节点的配置未开启。请参阅节点运维指南以及 config.conf 配置文件中的 vm.supportConstant 参数说明。 |
SERVER_BUSY — 节点缓存交易数量超限
当前节点缓存的交易总数超过 node.maxTransactionPendingSize 配置的上限时,广播请求会返回 SERVER_BUSY。该参数的默认值为 2000。
修复方案(自建节点):
根据服务器的内存和 CPU 负载,在 config.conf 中适当调高该上限:
node.maxTransactionPendingSize = 5000修改后需重启 FullNode 使配置生效。
说明使用 TronGrid 等托管 RPC 服务时,
SERVER_BUSY表示处理本次请求的后端节点当前无法接收更多待处理交易,不代表整个 TRON 网络繁忙。客户端可以采用指数退避重试,或切换到其他健康 Endpoint。
TronGrid 503 限流与服务不可用
TronGrid 对所有 HTTP 请求按 IP 和 API Key 限频。访问速率超过配额时,TronGrid 拒绝服务并返回 HTTP 4xx 或 5xx 错误码(最常见的是 HTTP 503 Service Unavailable)。
推荐的修复与优化对策:
- 务必在请求头携带
TRON-PRO-API-KEY:未携带 API Key 的请求频次阈值极低,很容易触发限流。Header 携带格式见 TronGrid 速率限制。 - 优化客户端请求频次:降低 DApp 启动时的突发并发。不要以快于出块的速度轮询节点——TRON 主网出块间隔 3 秒,快于 3 秒的轮询没有业务意义。
- 严格遵守
Retry-After响应头:503 响应包含Retry-After头时,客户端必须等待其指定时间后再重试。详见重试策略说明。 - 升级套餐或混合方案:高流量的生产业务,可在 TronGrid 控制台提交定制申请提升 API Key 的 QPS 上限,或采用自建节点 + 第三方托管 RPC 的混合配置。
广播后查询不到交易时,如何安全重试?
调用广播接口后,接口可能返回 {"result": true, "txid": "..."},也可能因请求超时而没有返回明确结果。如果后续暂时查询不到原交易,其状态仍无法确定。此时应保留原 txID,不要立即创建新交易。
result: true 只表示处理本次广播请求的节点未返回错误,不能证明交易已经被打包、执行成功或完成固化;请求超时时,也无法确定节点是否已经接收交易。
/walletsolidity/gettransactionbyid 返回空对象 {},只表示本次查询未找到已固化的原交易。在尚未确认最新固化区块时间和历史查询范围时,仅本地时间超过 raw_data.expiration、查询超时或一次空响应,都不能证明原交易未进入已固化主链。
广播后查询不到原交易时,应按以下流程核对其是否已进入固化主链:
-
只重播原交易:保留原
txID和已签名交易。在完成以下核对前,如需再次广播,只向可靠、已同步的节点提交完全相同的已签名交易。节点仍会执行正常的交易校验,因此重播不保证交易会被接受。重播不会改变txID;节点可能返回DUP_TRANSACTION_ERROR,但该错误不能证明交易已经上链。 -
确定起始区块:
- 如果业务系统在首次广播前记录了当时的最新固化区块,将该区块作为起始区块。
- 如果只保存了发送日志,并且日志覆盖所有广播尝试、系统时钟也已同步,将时间戳早于最早发送时间的最后一个固化区块作为起始区块。
raw_data.timestamp由客户端填写,不能代替实际发送时间。 - 如果上述信息均无法确定,先计算
raw_data.expiration减去 24 小时所对应的时间点,找到时间戳首次达到或超过该时间点的固化区块,并将其下一个区块作为起始区块。java-tron 在接收交易和校验区块内的交易时,都会拒绝expiration比当时链头区块时间晚超过 24 小时的交易;校验区块内的交易时,链头区块是待校验区块的前一个区块。因此,时间戳首次达到或超过该时间点的固化区块本身不能包含原交易,其下一个区块才是原交易最早可能进入的区块。
-
选择查询源并确认查询范围:选择一个可靠、已同步,并且同时提供
/walletsolidity/getnowblock和/walletsolidity/gettransactionbyid的节点或 RPC 服务作为查询源。先根据查询源的数据保留配置或其明确说明的历史查询范围,确认/walletsolidity/gettransactionbyid能够查询起始区块及之后各固化区块中的交易;如果无法确认,应更换查询源。确认查询源后,调用/walletsolidity/getnowblock,等待返回区块的block_header.raw_data.timestamp达到或超过原交易的raw_data.expiration,并将该区块作为结束区块。此时,原交易可能进入的区块已经全部固化,查询范围也已覆盖起始区块至结束区块。 -
查询原交易并决定后续操作:使用原
txID调用所选查询源的/walletsolidity/gettransactionbyid:- 如果查询到原交易,说明它已经进入已固化主链,不能再按“未上链”场景创建新交易。对于智能合约交易,进入已固化主链不等于执行成功;如需查看完整的执行结果、资源消耗等详情,再通过
/walletsolidity/gettransactioninfobyid查询已固化的交易信息。如果当前查询源无法返回该信息,应改用能够返回该信息的查询源。 - 如果接口返回空对象
{},由于第三步已经确认原交易可能进入的区块均已固化,且查询源覆盖完整范围,可以认定原交易未进入已固化主链,此时才可重新构建、签名并广播新交易。
- 如果查询到原交易,说明它已经进入已固化主链,不能再按“未上链”场景创建新交易。对于智能合约交易,进入已固化主链不等于执行成功;如需查看完整的执行结果、资源消耗等详情,再通过
相关资源
- 错误与调试说明 —— 详细了解 TRON API 错误分类、Hex 解码方法与重试机制
- 交易签名与广播——API 工作流 —— 剖析交易构建、离线签名与广播的完整生命周期
- 智能合约错误说明 —— 详细诊断因 TVM 虚拟机执行异常导致的链上失败
- 节点运维常见故障 —— 诊断与解决自建节点同步、内存及 P2P 连通性问题
- 问答中心 FAQ —— 归纳自 TRON 社区开发者的常见问题解答
Updated 19 days ago
