Errors

The Somnia DEX contracts revert with custom errors, not string messages. A failing eth_call (preflight simulation) or mined transaction returns ABI-encoded revert data whose first four bytes are the error selector - keccak256("ErrorName(argTypes)")[:4]. This page maps every selector to its error so integrators can decode a revert instead of guessing.

Decoding a revert

The revert data is selector (4 bytes) || abi.encode(args). Match the leading 4 bytes against the tables below.

With Foundry:

bash
# Decode a raw revert blob (selector + args)
cast decode-error 0xcf479181000000000000000000000000000000000000000000000000000000000000000a...

# Or look up just the selector
cast 4byte 0xcf479181

Two selectors reported most often during preflight are 0xf5e39c1f = IncorrectSender(address,address) (usually an eth_call sent without from, or placeOrderFor/cancelOrderFor from an unauthorized operator) and 0xcf479181 = InsufficientBalance(uint256,uint256) (the owner's free vault balance does not cover principal + max(makerFee, takerFee) + builderFee). A bare 0x revert with no selector is an out-of-gas or a low-level call failure - re-check the gas limit (see InsufficientGasForPayout on native-base BUYs).

SpotPool errors

Order validation

SelectorErrorMeaning / fix
0xaf608abbInvalidPrice(uint256 price, uint256 tickSize)price is not a whole multiple of tickSize. Tick-align the price - see Quantization.
0x4f174b29InvalidQuantity(uint256 quantity, uint256 constraint)quantity is not a whole multiple of lotSize. Lot-align the quantity.
0xeaa68cebQuantityBelowMinimum(uint256 quantity, uint256 minimum)quantity is below the pool's minQuantity.
0x2c5211c6InvalidAmount()Amount is zero or otherwise invalid.
0x57b0e210PriceTooLarge()Price exceeds the encodable range.
0xdf6c8c3dInvalidTakerSide()Taker side is inconsistent with the requested operation.
0xc04ad919FillOrKillNotFillable()A FOK order could not be filled in full at placement and was rejected.
0xe967701dZeroQuoteFillsAllowed()Order would produce a zero-quote fill. Increase size or improve price.

Funding and vault

SelectorErrorMeaning / fix
0xcf479181InsufficientBalance(uint256 available, uint256 required)Free vault balance does not cover principal + max(makerFee, takerFee) + builderFee. Fees debit the free balance, not the locked principal. See free-balance requirement.
0x890a6062UseDepositNative()Called deposit(NATIVE_TOKEN, ...) on a native-side pool. Call depositNative() with msg.value instead.
0x6ee800fcUnexpectedNativeDeposit()Sent msg.value to a fully-ERC20 pool.
0x1f89f671InvalidMsgValue(uint256 expected, uint256 received)msg.value does not match the required native input.
0x3022f2e4NativeTokenTransferFailed()A native payout call was rejected by the recipient.
0x734b5f70InvalidDepositOrWithdrawal()Token is neither the base nor quote of the market, or amount is invalid.

Gas

SelectorErrorMeaning / fix
0x782b2567InsufficientGasForPayout(uint256 gasLeft)Native-base BUY did not clear the payout gas headroom guard. Set the tx gas limit ≥ 5,000,000 and simulate with the same limit. See Contract Specifications.

Order lifecycle

SelectorErrorMeaning / fix
0xd4ec9006ExpiredOrderMustBeCancelled(uint128 orderId)Called reduceOrder on a past-expiry order. Use cancelOrder.
0x71fa8de6OrderIdMismatch()The stored order for that slot no longer matches the supplied OrderId (already filled, cancelled, or expired-and-reused). Re-read state with getOrder / getOwnOpenOrders before cancelling.
0x8080c2edIncorrectOrder()Order lookup did not resolve to a live order.
0xf5e39c1fIncorrectSender(address caller, address expected)Caller is not the order owner (or, for eth_call, no from was set). For operator flows the owner must authorize the operator - see below.

Batch and amend

SelectorErrorMeaning / fix
0xc2e5347dEmptyBatch()A batch method (placeOrders, cancelOrders, reduceOrders, amendOrders, and the ...For variants) was called with an empty array. Pass at least one request.
0x1d98a950AmendReplacementFailed()An amendOrder replacement would neither rest nor fill (crossed PostOnly, unfilled FOK, IOC no-fill, already-expired, or CancelTaker self-match). The amend reverts and the cancelled order is restored - the no-gap guarantee.
0xb16f4abbAmendOldOrderGone(uint128 oldOrderId)The amend's oldOrderId was already filled/cancelled by the time the tx landed and the request did not set alwaysPlace. Kept distinct from IncorrectSender so a maker racing a fill can branch to a re-quote. Set alwaysPlace = true to place the replacement anyway.

Operators and authorization

SelectorErrorMeaning / fix
0x3fb0ba2eOnlyApprovedContracts()Caller is not authorized for a ...For call. placeOrderFor admits a per-user approval or a system-contract allowlist; cancelOrderFor / reduceOrderFor admit the per-user approval only. See Operators.
0x118cdaa7OwnableUnauthorizedAccount(address)Caller is not the contract owner (admin-only entrypoint).

Builder codes

SelectorErrorMeaning / fix
0x41ec099fBuilderCodesNotSupported()Passed a non-zero builder while the protocol cap is 0. v1.0 ships with the cap disabled - pass address(0) and 0.
0x6d10926bInvalidBuilder()builderFeeBpsTimes1k != 0 while builder == address(0), or approved address(0).
0x0c9a7210BuilderNotApproved()Owner has not called approveBuilder for this builder.
0x528c953fBuilderFeeExceedsApproval()Per-order builder fee exceeds the owner's approval. Read getEffectiveBuilderApproval.
0xf559e808BuilderFeeExceedsCap()Approval or cap update exceeds the protocol-wide cap.
0xcd4e6167FeeTooHigh()Configured fee exceeds the allowed maximum.

Admin/config selectors (InvalidTickSize 0x747a60fb, InvalidLotSize 0x96a6cd0c, InvalidMinQuantity 0xe4c236aa, InconsistentMinQuantityAndLotSize 0xe94c2d7c, InvalidFeeRecipient 0x768dc598, InvalidMidpointEmaParameters 0xd8879b90, InvalidOperatorPermissionsRegistry 0x6e116665) are only reachable from owner-only entrypoints and should not appear during normal trading. Internal invariants (LinkedListCorrupted, ReentrancyGuardReentrantCall, allocator errors) indicate a bug - report them.

SpotStopOrderRegistry errors

See Stop Orders for the full mechanics.

SelectorErrorMeaning / fix
0xfa0d8bfdInsufficientSomiPayment()createPendingOrder requires msg.value == somiPaymentPerOrder() exactly (under- and over-payment both revert). Re-read it immediately before sending.
0x5eb24725NoActiveSubscription()Registry is dormant (createSubscription not called, or removeSubscription was called).
0xf5a46358TriggerTooCloseToEma()triggerPrice is within minStopDistanceBps of the current EMA midpoint. Move the trigger further out.
0x8b76b49cLimitPriceIncompatibleWithTrigger()For a LIMIT stop, limitPrice must be on the executable side of triggerPrice for the order direction.
0x5cd9bd3aInvalidLimitPrice()LIMIT stop with a zero/misaligned limitPrice, or a MARKET stop with a non-zero limitPrice (MARKET must pass 0).
0x42e1941cInvalidTriggerPrice()Trigger price is zero or otherwise invalid.
0x961ac3e3PriceNotAlignedToTickSize()limitPrice is not tick-aligned to the SpotPool.
0x4c4705caQuantityNotAlignedToLotSize()Quantity is not a multiple of the SpotPool's lotSize.
0x84c3adbeQuantityBelowMinimum()Quantity is below the SpotPool's minQuantity.
0x14ecd6c7InsufficientVaultBalance()Owner's SpotPool vault balance does not cover the order's collateral at creation time.
0xc32ff34dInvalidOrderOwner()order.owner does not match the caller.
0x5dcaf2d7OrderDoesNotExist()No pending order for that OrderId.
0x71fa8de6OrderIdMismatch()Supplied OrderId no longer matches stored state.
0x969bf728NothingToClaim()claimSomi() with a zero unclaimed balance.
0xe72f2f75SubscriptionStillActive()cancelInertOrders while the registry still has an active subscription.

Preflight (HTTP API) reverts

The HTTP prepare order endpoint simulates the transaction. A returned success: false / orderId: 0 means the simulated call reverted - decode the revert data with the tables above. Backend-side decoding of these reasons into the API response is tracked separately; until then, decode the selector client-side.