In Uniswap Universal Router, the allow-revert flag can let execution continue after a failed command. The failure must reach the router's success check; the flag does not catch every revert. Optional swap branches therefore need a verified failure boundary, explicit cleanup commands and balance reconciliation, even when the transaction receipt says success.
Find the boundary that contains the failure
Universal Router accepts a sequence of command bytes and a parallel array of encoded inputs. Each command uses the input at the same index. The official command reference describes the high bit, 0x80, as the allow-revert flag and recommends cleanup commands when unused funds can remain.
That description needs an implementation check. The discussion below uses Uniswap's router source at commit edf19bf, inspected on 28 October 2026. It explains that revision's execution paths; a real integration must identify its chain, deployed address and matching revision.
In the pinned UniversalRouter execution loop, dispatch returns a success value. A returned failure triggers ExecutionFailed when success is required. Setting the high bit permits the loop to continue after that returned failure.
A revert that escapes dispatch prevents the loop from reaching this decision. This matters for swaps: the inspected Dispatcher implementation calls its v3 swap implementation internally. Although the current command reference illustrates adding 0x80 to a direct v3 swap, that bit does not create a catch boundary around this internal call.
The same dispatcher executes EXECUTE_SUB_PLAN through a low-level call back into the router. A correctly encoded sub-plan can therefore return failure to its parent when a swap inside it reverts. The parent then applies its allow-revert setting. This is the boundary used in the example below.
The SDK's pinned command planner reinforces this distinction: its set of commands eligible for the planner's allowRevert option contains only EXECUTE_SUB_PLAN. Check the SDK version and contract together before relying on failure containment.
For this router revision, the command constants give EXECUTE_SUB_PLAN the value 0x21. Combining it with 0x80 produces 0xa1. The command identifier remains 0x21 after applying the revision's 0x7f mask. Those bytes identify a command and permission; they say nothing about the acceptable final holdings.
Two swap legs, one failed sub-plan
Consider this constructed, hypothetical route using ordinary tokens X and Y, each with six decimals. The wallet starts with 1,000 X and no Y; the router starts with neither token. Assume valid permissions, adequate gas, correctly encoded inputs and no other transfers. Gas is excluded from the token ledger.
One transaction contains these operations in order:
- Pull 1,000 X from the wallet into the router through a required funding command.
- Execute leg A: spend 300 X from the router, receive 597 Y into the router and enforce a 590 Y minimum.
- Execute a sub-plan containing leg B: attempt to spend the remaining 700 X, with a 1,380 Y minimum. The available output is only 1,370 Y, so the swap reverts.
- Sweep remaining X to the wallet, with a zero minimum.
- Sweep Y to the wallet, with a 590 Y minimum.
The output amounts are stipulated to isolate failure handling; they are not live quotes or results from an AMM pricing model. Both swap legs specify the router as payer and recipient. The sweep commands remain required outside the optional sub-plan.
Commands run in order. Allowing a branch to fail does not skip later commands when it succeeds; cleanup must work for both outcomes.
The minimum-output failure follows the pinned v3 swap module: exact-input output below its minimum causes a revert. The exact-input versus exact-output guide explains which side of a swap is fixed. Here, the question is what survives when one attempt fails.
Require leg B, and the earlier work rolls back
First encode the outer sub-plan command as 0x21, with success required. Leg A succeeds provisionally. Leg B then reverts, its sub-plan returns failure, and the parent rejects that failure. The transaction's funding transfer and leg A both roll back. Neither cleanup command is reached.
The final token balances return to 1,000 X and zero Y in the wallet, with zero X and Y in the router. This assumes the funding occurred inside this transaction. An approval or deposit completed in an earlier transaction lies outside this rollback.
This follows the EVM REVERT specification: state changes in reverted execution are discarded, while a caller can receive a failed-call result. Reverting token movements does not erase the computational work already performed. Retain the included transaction's actual gas charge separately.
Allow leg B to fail, and reconcile the surviving balances
Now change only the parent sub-plan byte to 0xa1. Its failure is allowed. Leg B's attempted input payment and output transfer roll back within its call boundary. Leg A, which sits outside that sub-plan, remains eligible to persist if later commands finish successfully.
| Stage in the allowed-failure case | Wallet X | Wallet Y | Router X | Router Y |
|---|---|---|---|---|
| Opening balances | 1,000 | 0 | 0 | 0 |
| Required funding completes | 0 | 0 | 1,000 | 0 |
| Leg A completes | 0 | 0 | 700 | 597 |
| Leg B sub-plan reverts | 0 | 0 | 700 | 597 |
| Required X sweep completes | 700 | 0 | 0 | 597 |
| Required Y sweep completes | 700 | 597 | 0 | 0 |
The wallet ultimately spends 1,000 − 700 = 300 X and receives 597 Y. Leg A's pools gain 300 X and release 597 Y, balancing the wallet's changes. The failed branch contributes no retained swap. Its provisional 1,370 Y must never become an acquired holding in the journal.
Grouping changes the outcome. If both legs sat inside the same failing sub-plan, leg A would roll back with leg B. Draw the call boundary around the operations that may disappear together before describing a route as supporting partial completion.
Cleanup defines an acceptable partial outcome
In the pinned Payments module, SWEEP reads the router's balance of the specified asset, checks it against a minimum and transfers the entire positive balance to the specified recipient. The minimum is a condition on the sweep; it is not the quantity to transfer.
In this illustration, the X sweep returns all 700 unused X. Its zero minimum also permits the sweep to complete with no transfer if another acceptable route outcome leaves zero X. The Y sweep transfers all 597 Y because that balance exceeds its 590 Y floor.
For a separate hypothetical success case, let leg B return 1,400 Y. Both legs then spend all 1,000 X and produce 597 + 1,400 = 1,997 Y. The zero-minimum X sweep does nothing, and the required Y sweep delivers 1,997 Y.
Omit both sweeps and the allowed-failure transaction can finish with the wallet holding neither token and the router holding 700 X plus 597 Y. The transaction succeeded, but the intended recipient received nothing. Uniswap's integration guidance warns that funds left in this permissionless contract can be taken by anyone. A later recovery attempt is not an adequate cleanup policy.
The Y floor also expresses which partial outcome is authorized. Change that required floor from 590 to 1,970 Y, the sum of the two leg minimums. After leg B fails, only 597 Y remains. The sweep fails and the whole transaction rolls back, including leg A and the preceding X sweep.
| Alternative version of the hypothetical plan | Final wallet X | Final wallet Y | Final router holdings |
|---|---|---|---|
| Required leg B sub-plan | 1,000 | 0 | Zero |
| Allowed leg B failure; both required sweeps pass | 700 | 597 | Zero |
| Allowed leg B failure; sweeps omitted | 0 | 0 | 700 X and 597 Y |
| Allowed leg B failure; required Y floor is 1,970 | 1,000 | 0 | Zero |
These are alternative plans, not consecutive attempts. The 590 and 1,970 Y floors illustrate different acceptance rules, not recommended settings. Choose the acceptable final token amounts before building the command sequence.
A whole-route amount floor also differs from a pool-path price boundary. The minimum-output versus price-limit guide follows that distinction. Permitting a branch to fail does not remove the need to define the accepted result.
Failures the flag cannot make harmless
At the outer entrypoint, the router checks enforce the deadline overload's expiry guard before entering the command loop. Mismatched outer command/input counts also fail before dispatch. Flags in that rejected outer plan cannot rescue either condition. A length mismatch inside an already-entered sub-plan is different: it reverts that subcall and can return failure to the parent. The deadline and slippage article explains why time and amount conditions need separate checks.
The dispatcher's decoding path can fail on malformed sub-plan parameters before making the low-level call. Invalid commands can also revert directly. Validate the outer encoding as well as the proposed swap failure; a high bit is insufficient proof of containment.
Later required operations can reject the surviving state. A failed token transfer during cleanup or an unmet final minimum can still revert the entire transaction. Keep disposal of unused funds outside the branch whose failure is optional, and verify that disposal under every permitted outcome.
The ordinary-token assumption matters here. Transfer fees and rebases can change the relationship between router debits, recipient receipts and later holdings. The fee-on-transfer and rebasing-token guide explains those additional reconciliation boundaries. This numerical example establishes no compatibility with such tokens.
Review commands as a balance ledger
Before signing or enabling an integration, complete a worksheet for the exact encoded plan:
| Evidence to record | Validation to perform |
|---|---|
| Chain, router address, revision and SDK version | Decode against the implementation actually called |
| Command index, input and high bit | Identify required operations and allowed returned failures |
| Sub-plan membership | Mark every operation that rolls back together |
| Funding source and each recipient | Locate each asset after success and permitted failure |
| Cleanup token, recipient and minimum | Verify all residual balances leave in acceptable outcomes |
| Final wallet amounts | Specify what partial completion is authorized to deliver |
Exercise successful branches, a failed optional branch and a failed required cleanup in simulation. Compare the resulting balances with the worksheet, including a branch that leaves no input to sweep. State can change before inclusion, so retain the mined transaction's evidence too.
After inclusion, reconcile completed transfers, relevant execution traces, recipient balances and gas. A receipt-level success flag cannot identify every completed leg; receipt status versus token-transfer evidence explains the evidence to preserve. Attribute balance changes to the transaction rather than unrelated wallet activity.
These are educational router mechanics, not a statement that KLINEO provides this integration. For any composed execution, require a clear answer for each asset before proceeding: which operation spent it, which failed boundary restored it, and which required command delivered the remainder to its intended recipient.
