# Error Handling

All SDK methods throw `ThetanutsError` or a typed subclass. Use the `code` property or `instanceof` to branch on specific errors.

## Error Codes

| Code                     | Description                  | Typical Cause                        |
| ------------------------ | ---------------------------- | ------------------------------------ |
| `ORDER_EXPIRED`          | Order has expired            | Order's expiry timestamp has passed  |
| `SLIPPAGE_EXCEEDED`      | Price moved beyond tolerance | Market moved during transaction      |
| `INSUFFICIENT_ALLOWANCE` | Token approval needed        | Haven't approved tokens for contract |
| `INSUFFICIENT_BALANCE`   | Not enough tokens            | Wallet balance too low               |
| `NETWORK_UNSUPPORTED`    | Chain not supported          | Using unsupported chain ID           |
| `HTTP_ERROR`             | API request failed           | Network issues, API downtime         |
| `BAD_REQUEST`            | Invalid API request          | Malformed parameters                 |
| `NOT_FOUND`              | Resource not found           | Invalid order ID or address          |
| `RATE_LIMIT`             | Rate limit exceeded          | Too many API requests                |
| `CONTRACT_REVERT`        | Contract call failed         | Transaction reverted on-chain        |
| `INVALID_PARAMS`         | Invalid parameters           | Wrong types, missing required fields |
| `INVALID_ORDER`          | Order validation failed      | Malformed order structure            |
| `ORDER_NOT_FOUND`        | Order doesn't exist          | Order ID not in orderbook            |
| `SIZE_EXCEEDED`          | Fill size too large          | Requested more than available        |
| `SIGNER_REQUIRED`        | Signer needed                | Write operation without signer       |
| `WEBSOCKET_ERROR`        | WebSocket error              | Connection issues                    |
| `KEY_NOT_FOUND`          | RFQ key missing              | No ECDH keypair generated            |
| `INVALID_KEY`            | Invalid key format           | Corrupted or wrong key format        |
| `ENCRYPTION_FAILED`      | Encryption failed            | ECDH encryption error                |
| `DECRYPTION_FAILED`      | Decryption failed            | Wrong key or corrupted data          |
| `UNKNOWN`                | Unknown error                | Unexpected error                     |

## Error Classes

### Base Class

```typescript
class ThetanutsError extends Error {
  readonly code: ThetanutsErrorCode;
  readonly cause?: unknown;
  readonly meta?: Record<string, unknown>;
}
```

### API Errors

```typescript
class APIError extends ThetanutsError {
  readonly status: number;  // HTTP status code
}

class BadRequestError extends APIError {}  // HTTP 400
class NotFoundError extends APIError {}    // HTTP 404
class RateLimitError extends APIError {}   // HTTP 429
```

### Contract Errors

```typescript
class ContractRevertError extends ThetanutsError {}
class InsufficientAllowanceError extends ThetanutsError {}
class InsufficientBalanceError extends ThetanutsError {}
class OrderExpiredError extends ThetanutsError {}
class SlippageExceededError extends ThetanutsError {}
```

### Client Errors

```typescript
class SignerRequiredError extends ThetanutsError {}
class InvalidParamsError extends ThetanutsError {}
class NetworkUnsupportedError extends ThetanutsError {}
class WebSocketError extends ThetanutsError {}
```

### RFQ Key Errors

```typescript
class KeyNotFoundError extends ThetanutsError {}   // KEY_NOT_FOUND
class InvalidKeyError extends ThetanutsError {}    // INVALID_KEY
class EncryptionError extends ThetanutsError {}    // ENCRYPTION_FAILED
class DecryptionError extends ThetanutsError {}    // DECRYPTION_FAILED
```

## Handling Errors with `isThetanutsError`

```typescript
import {
  isThetanutsError,
  ThetanutsClient,
} from '@thetanuts-finance/thetanuts-client';

try {
  await client.optionBook.fillOrder(orderWithSig);
} catch (error) {
  if (isThetanutsError(error)) {
    switch (error.code) {
      case 'ORDER_EXPIRED':
        console.log('Order has expired — fetch fresh orders');
        break;
      case 'INSUFFICIENT_ALLOWANCE':
        console.log('Approve tokens before filling');
        break;
      case 'SIGNER_REQUIRED':
        console.log('Initialize client with a signer');
        break;
      default:
        console.log(`SDK error [${error.code}]: ${error.message}`);
    }
  }
}
```

## Handling Errors with `instanceof`

Use typed error classes for branch-specific recovery logic:

```typescript
import {
  ThetanutsError,
  ContractRevertError,
  InsufficientAllowanceError,
  OrderExpiredError,
} from '@thetanuts-finance/thetanuts-client';

try {
  await client.optionBook.fillOrder(order, 10_000000n);
} catch (error) {
  if (error instanceof OrderExpiredError) {
    console.log('Order expired — fetching fresh orders...');
    const freshOrders = await client.api.fetchOrders();
    // retry with a fresh order

  } else if (error instanceof InsufficientAllowanceError) {
    console.log('Approving tokens...');
    await client.erc20.ensureAllowance(
      client.chainConfig.tokens.USDC.address,
      client.chainConfig.contracts.optionBook,
      10_000000n
    );
    // retry

  } else if (error instanceof ContractRevertError) {
    console.log('Contract reverted:', error.message);
    console.log('Cause:', error.cause);

  } else if (error instanceof ThetanutsError) {
    console.log(`SDK error [${error.code}]: ${error.message}`);
    console.log('Meta:', error.meta);  // Additional context
  }
}
```

## Retry Pattern for Transient Errors

Use exponential backoff for rate limiting and transient network errors:

```typescript
import { RateLimitError } from '@thetanuts-finance/thetanuts-client';

async function withRetry<T>(fn: () => Promise<T>, maxRetries = 3): Promise<T> {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      return await fn();
    } catch (error) {
      if (error instanceof RateLimitError) {
        const delay = Math.pow(2, attempt) * 1000; // exponential backoff
        console.log(`Rate limited, retrying in ${delay}ms...`);
        await new Promise((r) => setTimeout(r, delay));
        continue;
      }
      throw error; // non-retryable — rethrow immediately
    }
  }
  throw new Error('Max retries exceeded');
}

const orders = await withRetry(() => client.api.fetchOrders());
```

## Contract Revert Reasons

### OptionFactory

| Revert Reason             | Cause                         | Solution                                                    |
| ------------------------- | ----------------------------- | ----------------------------------------------------------- |
| `InvalidImplementation`   | Unknown option implementation | Use valid implementation from `chainConfig.implementations` |
| `InvalidCollateral`       | Unsupported collateral token  | Use USDC, WETH, or cbBTC                                    |
| `InvalidExpiry`           | Expiry in past or too far     | Set expiry between now and a reasonable future date         |
| `InvalidStrikes`          | Strike price invalid          | Ensure strikes are positive and properly scaled             |
| `OfferPeriodEnded`        | Offer period has closed       | Cannot make offer after `offerEndTimestamp`                 |
| `OfferPeriodNotEnded`     | Offer period still open       | Wait for period to end before revealing                     |
| `RevealPeriodEnded`       | Reveal window closed          | Reveal within the reveal window                             |
| `NoOffersToReveal`        | No offers submitted           | At least one MM offer required to settle                    |
| `QuotationAlreadySettled` | Already settled               | Cannot settle twice                                         |
| `NotRequester`            | Wrong caller                  | Only the requester can cancel their RFQ                     |

### OptionBook

| Revert Reason      | Cause               | Solution                |
| ------------------ | ------------------- | ----------------------- |
| `OrderExpired`     | Order has expired   | Fetch fresh orders      |
| `OrderCancelled`   | Order was cancelled | Use a different order   |
| `InsufficientSize` | Not enough to fill  | Reduce fill amount      |
| `InvalidOrder`     | Malformed order     | Re-fetch order from API |

### Option Contract

| Revert Reason      | Cause                  | Solution                                  |
| ------------------ | ---------------------- | ----------------------------------------- |
| `NotExpired`       | Option not expired yet | Wait for expiry before calling `payout()` |
| `AlreadySettled`   | Already settled        | Cannot payout twice                       |
| `NotBuyer`         | Caller is not buyer    | Only buyer can execute payout             |
| `NotBuyerOrSeller` | Wrong caller           | Only buyer/seller can close               |

### ERC20

| Revert Reason                            | Cause                | Solution                                      |
| ---------------------------------------- | -------------------- | --------------------------------------------- |
| `ERC20: insufficient allowance`          | Not approved         | Call `approve()` or `ensureAllowance()` first |
| `ERC20: transfer amount exceeds balance` | Insufficient balance | Check balance before transacting              |

## Common Issues

### INSUFFICIENT\_ALLOWANCE

Always call `ensureAllowance()` before `fillOrder()` or `requestForQuotation()`. The SDK does not auto-approve.

```typescript
await client.erc20.ensureAllowance(
  client.chainConfig.tokens.USDC.address,
  client.chainConfig.contracts.optionBook,
  requiredAmount
);
```

### ORDER\_EXPIRED

Check expiry before filling to avoid wasted gas:

```typescript
const now = Math.floor(Date.now() / 1000);
const validOrders = orders.filter(o => Number(o.order.expiry) > now + 60); // 60s buffer
```

### SIGNER\_REQUIRED

Initialize the client with a signer for any write operation:

```typescript
const client = new ThetanutsClient({
  chainId: 8453,
  provider,
  signer: wallet,  // required for fillOrder, approve, close, payout, etc.
});
```

Alternatively, use the `encode*` methods (e.g., `encodeFillOrder()`) to construct transaction data for an external wallet.

### KEY\_NOT\_FOUND

Generate or restore an ECDH keypair before creating RFQs:

```typescript
const keyPair = await client.rfqKeys.getOrCreateKeyPair();
// Keys are automatically persisted to disk (Node.js) or localStorage (browser)
```

### DECRYPTION\_FAILED

Verify you are using the same keypair that was used when the RFQ was created:

```typescript
const currentKeyPair = await client.rfqKeys.getOrCreateKeyPair();
const rfq = await client.api.getRFQFromRfq(quotationId);

if (currentKeyPair.compressedPublicKey !== rfq.requesterPublicKey) {
  console.error('Key mismatch — import the original private key via importFromPrivateKey()');
}
```

## Debugging

### Enable Debug Logging

```typescript
import { consoleLogger, ThetanutsClient } from '@thetanuts-finance/thetanuts-client';

const client = new ThetanutsClient({
  chainId: 8453,
  provider,
  logger: consoleLogger,
});
```

### Inspect Error Details

```typescript
import { ThetanutsError } from '@thetanuts-finance/thetanuts-client';

try {
  await client.optionBook.fillOrder(order);
} catch (error) {
  if (error instanceof ThetanutsError) {
    console.log('Code:', error.code);
    console.log('Message:', error.message);
    console.log('Cause:', error.cause);  // Original underlying error
    console.log('Meta:', error.meta);    // Additional SDK context
  }
}
```

## See Also

* [Token Operations](https://docs.thetanuts.finance/sdk/guides/token-operations) — `ensureAllowance` before fills
* [Production Checklist](https://docs.thetanuts.finance/sdk/guides/production-checklist) — error logging in production
* [WebSocket](https://docs.thetanuts.finance/sdk/guides/websocket) — `WEBSOCKET_ERROR` reconnection handling


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.thetanuts.finance/sdk/guides/error-handling.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
