When A Swap Is Refused
A swap either completes or it is refused. A refused swap is atomic — nothing moves, your balances are exactly what they were, and the only cost is gas. There is no partial fill and no half-completed state to unwind.
Refusals fall into two groups, and the difference decides what you do next.
Conditions you can fix yourself
Check these first:
The amount cannot be represented in the output token's precision
Converting from more decimal places to fewer will not round, so any remainder is refused.
What to do: Round your input down to a whole multiple of the output token's step before you quote. Read both tokens' decimals from the pool rather than from the token contracts — the pool uses the values it cached at registration. See Preview a swap.
The amount is below the minimum for the pair or for that direction
What to do: Increase it. Both minimums are readable before you swap — see Pool interface. A dust balance rounded down to zero is also refused, so guard for zero after rounding.
Your contract does not hold the input tokens
The pool pulls from whoever calls it, and in a contract-to-contract swap that is your contract, not your wallet.
What to do: Move the tokens into the contract first.
Your contract has not approved the pool to spend the input token
What to do: Approve before swapping, in the same transaction — see Execute a swap.
The output would be below the floor you set
What to do: Expected, and the floor doing its job. Re-quote and retry; the fee may have changed since your quote.
The recipient is not on your own allowlist
Your contract keeps its own list of permitted destinations, checked before anything reaches the pool.
What to do: Add it from your owner account. See Setup.
Your contract has not granted its own per-destination approval
Approvals are keyed on the caller, so one granted from your owner wallet is a record the swap never reads.
What to do: See Destination override approval.
The output is a stablecoin and you named a third-party recipient
Refused with STABLECOIN_MUST_SELF_DELIVER. Stablecoin output can only go to the account that did the
swap, and nothing on either list changes this — not an authorization we grant, and not an entry on
your contract's own allowlist, because the check runs ahead of both.
What to do: Deliver to your own contract and transfer onward yourself. Decide this before you deploy — see Setup.
Conditions we resolve
If you have ruled out everything above, please reach out to your Franklin Templeton contact and we will take it from there.
Most refusals can be identified before you spend gas. A pre-flight read tells you which condition would stop you, which is faster than interpreting a refusal — see Check eligibility.
Reading a refusal in your own code
Not every refusal decodes as readable text. Some arrive as an encoded identifier rather than a message, so a handler that only looks for text will show nothing useful and fall through to a generic failure. Handle the non-text case explicitly, even if all you do is surface the raw value.
Base your logic on your own pre-flight reads rather than on the wording of a refusal. The messages are not a stable interface — they can change, and the same underlying condition does not always produce identical wording. Your balance, your allowance, your rounding and the reserve are all readable, and they are the reliable basis for what your code does next.