Account Demolisher
Reference

Failure reference

The failure kinds a close can end with, what Retry does with each, and the typed errors the codebase raises.

A close that stops ends with a FailureKind (src/lib/orchestrator/failures.ts). The kind decides what Retry does:

  • Retryable: Retry runs the same plan again with the wallet connected now. Steps that already landed are skipped, and failed or skipped steps are reset and run again. A submission whose outcome was unknown is looked up first.
  • Not retryable: Retry rebuilds the plan from fresh discovery, so you review it again.

A Retry without a connected wallet also rebuilds.

Failure kinds

KindRaised whenRetryable
bad_seqThe account's sequence moved under a step (tx_bad_seq)Yes
changed_footprintThe ledger entries a Soroban step declared changed or were archived before it landedYes
networkHorizon, RPC or the mediator did not answer usefully: a timeout, a rate limit, a 5xx, an unreadable error page, tx_too_late, tx_insufficient_fee, unreadable fee stats, a Soroban RPC that kept answering TRY_AGAIN_LATER, or a DeFi protocol that could not be read before the mergeYes
signingThe wallet did not signYes
ambiguousA submission's outcome is unknown; Retry checks it on chain before signing anything againYes
forward_pendingThe merge into the forwarding account landed but the forward did not; Retry runs the forwardYes
interruptedYou stopped the close at a step boundaryYes
close_runningAnother run of the close for this account is still going, in this tab or anotherYes
not_mergeableThe account can no longer be merged as planned (account_merge blocked: ...), the destination can no longer receive a balance you chose to send as-is (Your destination can no longer receive ...), or Horizon refused a transaction with a code a replay cannot fixNo
position_openA DeFi position is still open at the merge, or a token could be neither converted nor sent as-isNo
revertedA Soroban step failed on chainNo
unsafeA step's transaction asked for more than the step needs (allow-list, authorization tree, exact shape or fee cap) and was not signedNo
bundle_staleA collected shared-account bundle can no longer land, or Horizon refused it; signatures are collected againNo
mediator_expiredThe forwarding authorization ran too low to sign the merge safelyNo
conversion_refusedA conversion would now sell over the price-impact cap or below the reviewed quote, or has no route; the rebuilt plan asks you per tokenNo
claim_unavailableA Blend backstop emissions claim that simulated at review can no longer run; the rebuilt plan asks you to forfeit the BLNDNo
unknownAnything unclassifiedNo

How an error gets its kind

classifyFailure resolves the kind in this order:

  1. A CloseFailure carries its kind.
  2. A Horizon rejection with result codes: tx_bad_seq is bad_seq; tx_too_late and tx_insufficient_fee are network, because a rebuild with a fresh time bound or a higher bid can land; every other code is not_mergeable.
  3. A Horizon NetworkError (not a 404) with status 429, a 5xx, or no readable status is network.
  4. The message, lowercased, matched in this order:
#PhrasesKind
1tx_bad_seq, txbadseqbad_seq
2position(s) still openposition_open
3account_merge blockednot_mergeable
4footprint, restorepreamble, entryarchived, entry_archived, entry_expiredchanged_footprint
5failed on-chainreverted
6timed out, timeout of, fetch failed, failed to fetch, network error, networkerror, econnreset, econnrefused, 429, 502, 503, 504, bad gateway, service unavailable, gateway timeoutnetwork
7Anything elseunknown

The diverged-state stops (2 and 3) are checked before the footprint phrases, so a merge refusal whose detail mentions an archived entry is never retried as a footprint race.

Merge refusals

The close loop refuses the merge with messages that start account_merge blocked:. Each is not_mergeable, except the ones below:

Message after account_merge blocked:KindWhat Retry does
N Soroban DeFi position(s) still open (blend=…, backstop=…, aquarius=…, …)position_openRebuilds the plan, so the open positions are unwound
could not confirm your DeFi positions are all closed (…)networkRuns the same plan once the protocol can be read
could not re-check DeFi positions before the merge (…)networkRuns the same plan once discovery answers
could not read the balance of token … paid out by your DeFi exits (…)networkRuns the same plan once the token's balance can be read
N credit balance(s) can't be disposed of as reviewed (…), when any listed balance has a market but its price moved past the review or over the price-impact capconversion_refusedRebuilds the plan and asks for each balance: sell at the new price, send it as-is or return it to its issuer

The three network refusals are safety stops: a position or payout the close cannot read is never merged around and stranded.

Typed errors

ErrorRaised byCarries
CloseFailureThe executor and the execute actorkind
AmbiguousSubmitErrorA submission Horizon or RPC never confirmed either waytxHash, maxTime; kind ambiguous
SubmitRejectedErrorA classic submission Horizon refusedcodes, httpStatus
AllowlistViolationThe contract allow-listviolations, every blocked invocation with its reason
UnsafeDefiAuthErrorThe DeFi authorization-tree guardnodeKind and a reason
UnsafeRevokeError, UnsafeTransferErrorThe revoke and token-transfer guardsA reason, prefixed Refusing to sign revoke: or Refusing to sign token transfer:
SorobanFeeCapErrorThe Soroban fee capfeeClass and the fee breakdown
SignedEnvelopeErrorThe check on a wallet's signed envelopeA reason
LedgerSigningErrorA Ledger refusalrefusal: rejected, hash-signing-disabled, too-large or unparseable
PriceImpactRefusedErrorA conversion over the price-impact cap or worse than the reviewed quotereason, priceImpactBps, expectedOut
NoSoroswapRouteErrorA held-token conversion with no router path to XLMA message
BackstopEmissionsClaimErrorA backstop emissions claim that no longer simulatespoolId, accruedEmissions
SimulationErrorSoroban simulationerrorCode, diagnostic events
SimulationFailedError, NodeNotBuiltErrorPlan simulationThe node and the upstream error
SorobanNotQueuedErrorAn RPC that did not queue a Soroban transactionstatus, resultCode
AccountNotFoundErrorThe account audit, on a Horizon 404publicKey
AllowanceScanIncompleteError, AllowanceScanOverflowErrorAn allowance scan that did not cover its window, or a contract cut to 20 spendersThe partial list and what was left out
TooManySignaturesErrorAn envelope already at the 20-signature limitA message
PendingForwardStorageErrorBrowser storage refusing a forward recovery recordA message; the close does not start
SlippageConfigErrorA set but invalid NEXT_PUBLIC_SLIPPAGE_BPS or NEXT_PUBLIC_MIN_MARKET_OUT_STROOPSA message; the build fails
ClientIpUnavailableErrorA TRUSTED_PROXY_HOPS or proxy setup that cannot identify the clientA message; the server refuses to start, or the route answers 500
BodyTooLargeErrorA request body over its route's caplimit

Reading an unknown error

errorMessage(err, fallback) in src/lib/errors.ts returns, in order: an Error's non-empty message, a non-empty string, the non-empty string message of a plain object, then the fallback, or String(err) when there is none.

The plain-object case exists because the Soroban RPC client rejects with a { code, message } object rather than an Error, so an instanceof Error check alone discards the real message.

Slippage and price bounds

ConstantValueMeaning
DEFAULT_SLIPPAGE_BPS1001% below the quote is the minimum accepted output
MIN_SLIPPAGE_BPS, MAX_SLIPPAGE_BPS10, 500The range NEXT_PUBLIC_SLIPPAGE_BPS accepts
MAX_PRICE_IMPACT_BPS200A full-size conversion more than 2% below the reference rate needs your decision
REFERENCE_TRADE_DIVISOR1000The reference trade is 1/1000 of the balance

On this page