# Error Reference

Where failures surface depends on the path you are on:

| Path | Failure appears as |
| :--- | :--- |
| Onchain | A revert on your transaction, or a `PendingTradeRefund` / rejection event during settlement |
| Gasless (1CT) | A `400` at submission — or `404` for `Position not found` — or `skipped` / `success = false` in the [intent status](/gasless/submit#poll-status) |
| REST API | An HTTP status code |

## Contract errors

Custom errors raised by the protocol. On the onchain path these revert your transaction; on the 1CT
path they arrive as encoded revert data in the status `reason` field.

### Market and pair state

| Error | Meaning |
| :--- | :--- |
| `MarketClosed` | The market is outside its trading hours |
| `PairClosed` | Trading is disabled for this pair |
| `UnsupportedChain` | Operation not supported on this network |

### Collateral and amounts

| Error | Meaning |
| :--- | :--- |
| `UnsupportedToken` | Token not accepted by the protocol |
| `UnsupportedMarginToken` | Token cannot be used as margin — usually a `tokenIn` / `lvToken` mismatch |
| `InvalidAmount` | Amount is zero, below the minimum, or does not cover the fee |
| `InsufficientMarginAmount` | Position does not hold enough margin for the operation |
| `InsufficientLiquidityPool` | Pool cannot back the requested size |

### Leverage and risk

| Error | Meaning |
| :--- | :--- |
| `BelowDegenModeMinLeverage` | Resulting leverage below the pair's minimum |
| `ExceedMaxLeverageAfterMarginRemoval` | Removal would push leverage past the maximum |
| `LiquidatableAfterMarginRemoval` | Removal would make the position immediately liquidatable |
| `ExceedMaxTakeProfitAfterMarginRemoval` | Existing take-profit would exceed its bound at the new leverage |

### Positions and orders

| Error | Meaning |
| :--- | :--- |
| `NonexistentTrade` | Position does not exist — already closed, or the wrong hash |
| `PositionNotCloseable` | Cannot be closed yet — check `earliestCloseTime` |
| `InvalidStopLoss` | Stop-loss price invalid for this position |
| `TooManyActiveDecreaseOrders` | Per-position TP/SL order cap reached |
| `CoolingOffPeriod` | Temporarily unavailable |

### Oracle

| Error | Meaning |
| :--- | :--- |
| `InvalidPrice` | Oracle returned an unusable price |
| `InvalidUpdateFee` | `msg.value` does not cover the oracle update fee |

### 1CT authorization

| Error | Meaning |
| :--- | :--- |
| `UnauthorizedOperation` | The intent targets a position the `trader` does not own |

::: warning Authorization failures are not custom errors
A bad signature, an unauthorized agent, a missing permission bit, and an expired `deadline` do
**not** revert with a custom error. The intent is skipped with `skipReason = INVALID` and the
transaction succeeds — see
[Interpreting the result](/gasless/submit#interpreting-the-result). Do not write a handler that
matches on a revert selector for these; match on `skipReason`.
:::

Some failures surface as `Error(string)` instead of a custom error, most notably
`OneClickV2: name taken` when [authorizing an agent](/gasless/authorization#authorize) with a name
already in use.

## Refund reasons

Carried by `PendingTradeRefund`, `ExecuteLimitOrderRejected` and `LimitOrderRefund` as an enum. A
refund means the request was rejected at settlement and collateral was returned — nothing reverted.

| # | Reason | Meaning |
| :--- | :--- | :--- |
| 0 | `NO` | No refund |
| 1 | `SWITCH` | Feature disabled |
| 2 | `PAIR_STATUS` | Pair not tradable |
| 3 | `AMOUNT_IN` | Collateral amount invalid |
| 4 | `USER_PRICE` | Fill fell outside the acceptable price — the most common one |
| 5 | `MIN_NOTIONAL_USD` | Position size below the minimum |
| 6 | `MAX_NOTIONAL_USD` | Position size above the maximum |
| 7 | `MAX_LEVERAGE` | Leverage above the tier's maximum |
| 8 | `TP` | Take-profit invalid |
| 9 | `SL` | Stop-loss invalid |
| 10 | `PAIR_OI` | Open interest cap for the pair reached |
| 11 | `OPEN_LOST` | Position would open already in loss beyond the allowed bound |
| 12 | `SYSTEM` | Internal rejection |
| 13 | `FEED_DELAY` | Oracle price too stale |
| 14 | `PRICE_PROTECTION` | Price protection triggered |
| 15 | `RESERVE_TOKEN_NOT_ACTIVE` | Collateral token disabled |
| 16 | `LV_TOKEN_NOT_ACTIVE` | Settlement token disabled |
| 17 | `MIN_LEVERAGE` | Leverage below the minimum |

`USER_PRICE` usually means the slippage bound was too tight or the price moved between quoting and
settlement. Widen the bound, or re-quote closer to submission.

## 1CT skip reasons

| `skipReason` | Meaning | Fix |
| :--- | :--- | :--- |
| `INVALID` | Signature invalid, agent unauthorized, permission bit missing, or deadline passed | Check authorization, deadline, EIP-712 structure and `primaryType` |
| `NONCE` | Nonce not strictly increasing, or outside the time window | Re-sign with a larger nonce |
| `FEE` | Execution fee could not be collected | Check fee-token balance and Diamond allowance |
| `UNKNOWN` | Unclassified | Report it |

See [Submit & Track](/gasless/submit#interpreting-the-result).

## 1CT submission errors

All return `400` except `Position not found`, which returns `404`. If you branch on the status code
to decide whether to retry, note that the one retryable failure is the `404`.

| Message contains | Cause |
| :--- | :--- |
| `deadline already passed` | `deadline` is in the past |
| `nonce too old` | Nonce more than 2 days old |
| `nonce too far in future` | Nonce more than 5 minutes ahead |
| `invalid action` | Action outside `[0, 13]` |
| `invalid … address` | Malformed `trader` or `feeToken` |
| `must not be empty` | Empty `actionData` or `signature` |
| `antiDdosFee mismatch` | Fee config cache older than ~120 seconds |
| `feeToken not supported` | Token not configured for this action |
| `anti-ddos not enabled` | Free action signed with a non-zero fee, **or** with a `feeToken` other than `0x0000…0000` — a disabled action needs both to be zero |
| `Position not found` (`404`) | Position not yet indexed — [see the caveat](/gasless/actions#actions-that-require-an-indexed-position) |

## Solidity panics

Occasionally surfaced through the 1CT `reason` field.

| Code | Meaning |
| :--- | :--- |
| `0x01` | Assertion failed |
| `0x11` | Arithmetic overflow or underflow |
| `0x12` | Division by zero |
| `0x21` | Invalid enum value |
| `0x22` | Invalid storage encoding |
| `0x31` | Pop from an empty array |
| `0x32` | Array index out of bounds |
| `0x41` | Out of memory |
| `0x51` | Call to an uninitialized function |

## Decoding revert data

```ts
import { decodeErrorResult, type Abi } from 'viem'

const STANDARD_ERRORS = [
  { type: 'error', name: 'Error', inputs: [{ name: 'message', type: 'string' }] },
  { type: 'error', name: 'Panic', inputs: [{ name: 'code', type: 'uint256' }] },
] as const satisfies Abi

const PROTOCOL_ERRORS = [
  { type: 'error', name: 'MarketClosed', inputs: [] },
  { type: 'error', name: 'NonexistentTrade', inputs: [] },
  { type: 'error', name: 'InsufficientLiquidityPool', inputs: [] },
  // …add the errors your integration surfaces
] as const satisfies Abi

export function decodeRevert(data: `0x${string}`): string {
  for (const abi of [STANDARD_ERRORS, PROTOCOL_ERRORS]) {
    try {
      const decoded = decodeErrorResult({ abi, data })
      if (decoded.errorName === 'Error') return String(decoded.args?.[0])
      if (decoded.errorName === 'Panic') return `Panic 0x${(decoded.args?.[0] as bigint).toString(16)}`
      return decoded.errorName
    } catch {
      // try the next ABI
    }
  }
  return 'Unknown error'
}
```

## HTTP status codes

| Code | Meaning |
| :--- | :--- |
| `200` | Success |
| `400` | Invalid parameters |
| `404` | Resource not found |
| `500` | Server error — retry with backoff |
