Skip to main content

Who this is for

You run a settlement agent and you are looking at a code in your own logs or traces. This page explains what the agent meant by it. These codes are internal to the agent you operate. None of them is returned to a payer.
Two of these strings, NONCE_TOO_LOW and BROADCAST_REJECTED, are also names in the settlement outcome the agent reports to Atum, where both map to the public code SETTLEMENT_FAILED. A name coinciding does not make the two the same thing. On this page they always mean the agent-internal condition.

Where you see these codes

Retryable and terminal

The agent classifies every failure as one of two things, and that decides whether the work is retried or abandoned.
  • Retryable — a transient condition. The agent retries under its own retry policy. Usually no action from you.
  • Terminal — provably permanent. The agent stops without retrying and routes the settlement to a terminal outcome. Almost always needs you.
An unclassified error is treated as retryable, so an unknown condition is never permanently abandoned on a guess. A permanent failure therefore has to be declared terminal explicitly, and a condition not listed as terminal below is not thereby safe to retry. The one carve-out is after a broadcast attempt. There an unclassified error is treated as terminal, because the transaction may already be in the mempool and retrying would broadcast a second one.

Signer codes

Raised by the signing layer on any chain. The code is prefixed onto the message, so a log line reads TURNKEY_RATE_LIMITED: .... All of them are terminal except the three Turnkey codes marked retryable below.

Turnkey signer

Only relevant if you sign through Turnkey.

Node submission codes

Set when the agent hands a signed transaction to an RPC node, and when the node rejects it. Each chain classifies independently, so the same physical event can land under different codes on different chains. Unlike the signer codes, these are not prefixed onto the message. Read them from the errorCode log field or the error.code span attribute. The three nonce codes and INSUFFICIENT_FUNDS need a word each:
  • NONCE_TOO_LOW — the nonce is already consumed. On a retry this means the earlier transaction mined, and the reconciler resolves it. Nothing to do.
  • NONCE_TOO_HIGH — an earlier nonce is still pending. Nothing to do.
  • REPLACEMENT_UNDERPRICED — a replacement was priced below the node’s bump threshold. The agent re-bumps at the same nonce. Nothing to do.
  • INSUFFICIENT_FUNDS — terminal. Top up the signing address’s native gas balance on that chain.

Rejected and uncertain are not the same thing

This is the distinction worth internalizing, because it decides whether the agent waits or retries. Rejected means the transaction is definitively not in flight. An RPC gateway returning 503, “no healthy upstream”, or 502 means the request never reached a chain node at all. The agent releases the fulfillment lock and the nonce reservation and fails fast, and Temporal retries the activity. Uncertain means the agent does not know. A 504, or a timeout after the request reached upstream, could mean the transaction landed. The agent keeps the fulfillment lock and the nonce reservation and hands off to the reconciler, which watches the chain to find out. Phantom is a stricter check on top of a successful broadcast, and it exists on EVM only. Transaction hashes are computed client-side, so a 200 OK does not prove the node accepted or propagated anything. Some RPC providers return success and silently drop the transaction. After broadcasting, the agent polls the node for the transaction it just sent. If the node does not return it within the verification window, that is a phantom.

What each bucket does

Releasing the reservation frees the ledger row, not the nonce value. A retry reads the nonce off the prior attempt and reuses it, and on EVM, where the signed bytes were persisted, re-broadcasts those same bytes rather than signing something new. One hash per nonce is what makes a single receipt conclusive.

Sizing the verification window

You do not set the phantom verification window directly. It is derived as 60% of attemptTimeoutMs, the per-chain plugin setting in your chain configuration, which defaults to 60000 ms and gives a 36000 ms window.
Size attemptTimeoutMs from your RPC provider’s read-your-own-write latency, not from block time. A provider that load-balances reads across nodes can take seconds to serve back a transaction it just accepted, on any chain, however fast that chain produces blocks. A window shorter than that latency turns healthy broadcasts into phantoms, and the retry re-broadcasts at the same nonce. 6000 ms is a working floor for attemptTimeoutMs on a public RPC endpoint. Raise it for a provider known to be slow.

Chain differences to know about

Two events are classified differently depending on the chain, and both matter operationally.
  • A node that acknowledges a broadcast without actually holding the transaction is BROADCAST_PHANTOM on EVM, BROADCAST_UNCERTAIN on Tron, and is not detected on Solana.
  • A contract revert seen at submit time is terminal on EVM and Tron, and retryable on Solana.

Next steps

Troubleshooting

The payer-facing errors, for when the problem is a payment rather than your agent

Configuration

Profile and chain configuration for the agent