广播与 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_ERRORraw_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 详细解析

该错误码是一个通用的静态校验失败标识,真正的底层业务原因通常存放在 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)。

推荐的修复与优化对策:

  1. 务必在请求头携带 TRON-PRO-API-KEY:未携带 API Key 的请求频次阈值极低,很容易触发限流。Header 携带格式见 TronGrid 速率限制。
  2. 优化客户端请求频次:降低 DApp 启动时的突发并发。不要以快于出块的速度轮询节点——TRON 主网出块间隔 3 秒,快于 3 秒的轮询没有业务意义。
  3. 严格遵守 Retry-After 响应头:503 响应包含 Retry-After 头时,客户端必须等待其指定时间后再重试。详见重试策略说明。
  4. 升级套餐或混合方案:高流量的生产业务,可在 TronGrid 控制台提交定制申请提升 API Key 的 QPS 上限,或采用自建节点 + 第三方托管 RPC 的混合配置。

广播后查询不到交易时,如何安全重试?

调用广播接口后,接口可能返回 {"result": true, "txid": "..."},也可能因请求超时而没有返回明确结果。如果后续暂时查询不到原交易,其状态仍无法确定。此时应保留原 txID,不要立即创建新交易。

result: true 只表示处理本次广播请求的节点未返回错误,不能证明交易已经被打包、执行成功或完成固化;请求超时时,也无法确定节点是否已经接收交易。

/walletsolidity/gettransactionbyid 返回空对象 {},只表示本次查询未找到已固化的原交易。在尚未确认最新固化区块时间和历史查询范围时,仅本地时间超过 raw_data.expiration、查询超时或一次空响应,都不能证明原交易未进入已固化主链。

广播后查询不到原交易时,应按以下流程核对其是否已进入固化主链:

  1. 只重播原交易:保留原 txID 和已签名交易。在完成以下核对前,如需再次广播,只向可靠、已同步的节点提交完全相同的已签名交易。节点仍会执行正常的交易校验,因此重播不保证交易会被接受。重播不会改变 txID;节点可能返回 DUP_TRANSACTION_ERROR,但该错误不能证明交易已经上链。

  2. 确定起始区块:

    • 如果业务系统在首次广播前记录了当时的最新固化区块,将该区块作为起始区块。
    • 如果只保存了发送日志,并且日志覆盖所有广播尝试、系统时钟也已同步,将时间戳早于最早发送时间的最后一个固化区块作为起始区块。raw_data.timestamp 由客户端填写,不能代替实际发送时间。
    • 如果上述信息均无法确定,先计算 raw_data.expiration 减去 24 小时所对应的时间点,找到时间戳首次达到或超过该时间点的固化区块,并将其下一个区块作为起始区块。java-tron 在接收交易和校验区块内的交易时,都会拒绝 expiration 比当时链头区块时间晚超过 24 小时的交易;校验区块内的交易时,链头区块是待校验区块的前一个区块。因此,时间戳首次达到或超过该时间点的固化区块本身不能包含原交易,其下一个区块才是原交易最早可能进入的区块。
  3. 选择查询源并确认查询范围:选择一个可靠、已同步,并且同时提供 /walletsolidity/getnowblock 和 /walletsolidity/gettransactionbyid 的节点或 RPC 服务作为查询源。先根据查询源的数据保留配置或其明确说明的历史查询范围,确认 /walletsolidity/gettransactionbyid 能够查询起始区块及之后各固化区块中的交易;如果无法确认,应更换查询源。确认查询源后,调用 /walletsolidity/getnowblock,等待返回区块的 block_header.raw_data.timestamp 达到或超过原交易的 raw_data.expiration,并将该区块作为结束区块。此时,原交易可能进入的区块已经全部固化,查询范围也已覆盖起始区块至结束区块。

  4. 查询原交易并决定后续操作:使用原 txID 调用所选查询源的 /walletsolidity/gettransactionbyid:

    • 如果查询到原交易,说明它已经进入已固化主链,不能再按“未上链”场景创建新交易。对于智能合约交易,进入已固化主链不等于执行成功;如需查看完整的执行结果、资源消耗等详情,再通过 /walletsolidity/gettransactioninfobyid 查询已固化的交易信息。如果当前查询源无法返回该信息,应改用能够返回该信息的查询源。
    • 如果接口返回空对象 {},由于第三步已经确认原交易可能进入的区块均已固化,且查询源覆盖完整范围,可以认定原交易未进入已固化主链,此时才可重新构建、签名并广播新交易。

相关资源