# Scallop Error Handling

> Scallop SDK error handling and exception hierarchy. Use when user says "error", "exception", "error code", "handle errors", "ScallopError", "transaction failed", "debug", "troubleshoot", or asks about Scallop error handling, error codes, debugging failed transactions, or understanding SDK exceptions.

- Skill: `scallop-io/scallop-error-handling` (Agent Skill)
- Install (CLI): `npx skillmds@latest add scallop-io/scallop-error-handling`
- Raw SKILL.md: https://api.skillmd.com/api/skills/scallop-io/scallop-error-handling/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- License: MIT
- Author: scallop-io (https://skillmd.com/u/scallop-io)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/scallop-io/scallop-error-handling

---


# Error Handling

Understand and handle errors from the Scallop Python SDK.

## Exception Hierarchy

```
ScallopError (base)
├── ScallopConfigError          — Configuration/setup issues
├── ScallopQueryError           — Read operation failures
│   └── ObligationNotFoundError — Obligation doesn't exist on-chain
├── ScallopTransactionError     — Transaction execution failures
│   ├── ObligationLockedError   — Obligation staked in borrow incentive
│   ├── InsufficientBalanceError— Not enough coins
│   ├── ZeroAmountError         — Amount must be > 0
│   └── LiquidationError        — Liquidation failures
│       └── NotLiquidatableError— Position is healthy
├── UnsupportedCoinError        — Unknown coin name/type
├── OraclePriceError            — Price feed failure
└── NetworkError                — RPC connection failures
```

All exceptions inherit from `ScallopError`, so you can catch broadly or narrowly.

## Import and Basic Usage

```python
# These are in the public API (importable from sui_scallop_sdk directly):
from sui_scallop_sdk import (
    ScallopError,
    ScallopConfigError,
    ScallopQueryError,
    ScallopTransactionError,
    ObligationNotFoundError,
    UnsupportedCoinError,
)

# These must be imported from the exceptions module directly:
from sui_scallop_sdk.exceptions import (
    ObligationLockedError,
    InsufficientBalanceError,
    ZeroAmountError,
    OraclePriceError,
    LiquidationError,
    NotLiquidatableError,
    NetworkError,
    parse_scallop_error,
)
```

## Exception Details

### ScallopError (Base)

```python
class ScallopError(Exception):
    message: str
    code: int | None = None
```

All SDK exceptions carry a `message` and optional numeric `code`.

### ScallopConfigError

Raised when SDK configuration is invalid.

```python
try:
    config = ScallopConfig(network="invalid")
except ScallopConfigError as e:
    print(f"Config error: {e}")
```

### ObligationNotFoundError

```python
try:
    obligation = query.get_obligation("0xnonexistent")
except ObligationNotFoundError as e:
    print(f"Not found: {e.obligation_id}")
```

**Fields**: `obligation_id: str`

### ObligationLockedError

Raised when an obligation is staked in the borrow incentive program and you try to modify it without unstaking first.

```python
try:
    tx.withdraw_collateral_quick(amount, "sui", obligation_id, obligation_key)
except ObligationLockedError as e:
    print(f"Obligation {e.obligation_id} is locked (code: {e.code})")
    # Solution: unstake obligation first
    # tx.unstake_obligation(obligation_id, obligation_key)
```

**Fields**: `obligation_id: str`, `code: int = 770`

### InsufficientBalanceError

```python
try:
    tx.supply_quick(1_000_000_000_000, "sui", wallet_address)
except InsufficientBalanceError as e:
    print(f"Need {e.required} {e.coin_name}, have {e.available}")
```

**Fields**: `coin_name: str`, `required: int`, `available: int`

### UnsupportedCoinError

```python
try:
    tx.supply_quick(100, "unknown_coin", wallet_address)
except UnsupportedCoinError as e:
    print(f"Unsupported: {e.coin}")
```

**Fields**: `coin: str` (stored from the `coin_name_or_type` constructor arg)

### ZeroAmountError

```python
try:
    tx.supply_quick(0, "sui", wallet_address)
except ZeroAmountError as e:
    print(f"Zero amount for operation: {e.operation}")
```

**Fields**: `operation: str`, `code: int = 1537`

### OraclePriceError

```python
try:
    tx.update_asset_prices_quick(["sui"])
except OraclePriceError as e:
    print(f"Oracle error for {e.coin_name}: {e}")
```

**Fields**: `coin_name: str`, `details: str | None`

### NotLiquidatableError

```python
try:
    tx.liquidate_quick(obligation_id, amount, "usdc", "sui", wallet_address)
except NotLiquidatableError as e:
    print(f"Position healthy — risk: {e.risk_level:.4f}")
```

**Fields**: `obligation_id: str`, `risk_level: float`

### NetworkError

```python
try:
    client = ScallopClient(rpc_url="https://bad-rpc.example.com")
except NetworkError as e:
    print(f"Cannot connect: {e.message} (URL: {e.rpc_url})")
```

**Fields**: `message: str`, `rpc_url: str | None`

## Parsing Raw Errors

The SDK provides `parse_scallop_error()` to convert raw error strings (from RPC responses) into typed exceptions:

```python
from sui_scallop_sdk.exceptions import parse_scallop_error

raw_error = "MoveAbort(0x...::lending::770)"

parsed = parse_scallop_error(raw_error)
if isinstance(parsed, ObligationLockedError):
    print("Obligation is locked in borrow incentive!")
```

This is useful when catching generic transaction failures and wanting to provide specific recovery suggestions.

## On-Chain Error Codes

| Code | Exception | Meaning |
|------|-----------|---------|
| 770 | `ObligationLockedError` | Obligation staked in borrow incentive |
| 1537 | `ZeroAmountError` | Amount must be greater than zero |

The full error code reference is at [Error Codes](../../references/error-codes.md).

## Error Handling Patterns

### Broad Catch

```python
try:
    result = builder.sign_and_send_tx_block(tx)
except ScallopError as e:
    print(f"Scallop error: {e}")
    if e.code:
        print(f"Error code: {e.code}")
```

### Specific Recovery

```python
from sui_scallop_sdk.exceptions import (
    InsufficientBalanceError,
    ObligationLockedError,
    OraclePriceError,
)

try:
    result = builder.sign_and_send_tx_block(tx)
except InsufficientBalanceError as e:
    print(f"Need more {e.coin_name}: have {e.available}, need {e.required}")
    # Maybe split the operation into smaller amounts
except ObligationLockedError:
    # Unstake, then retry
    tx2 = builder.create_tx_block()
    tx2.unstake_obligation(obligation_id, obligation_key)
    builder.sign_and_send_tx_block(tx2)
    # Now retry original operation
except OraclePriceError as e:
    print(f"Price feed down for {e.coin_name}, retrying...")
    # Maybe wait and retry, or use a different RPC
except ScallopError as e:
    print(f"Unexpected error: {e}")
```

### Transaction with Full Error Handling

```python
from sui_scallop_sdk import ScallopClient
from sui_scallop_sdk.exceptions import (
    ScallopError,
    InsufficientBalanceError,
    ObligationLockedError,
    OraclePriceError,
    NetworkError,
)

def safe_borrow(client, obligation_id, obligation_key, amount, coin_name):
    """Borrow with comprehensive error handling."""
    builder = client.create_builder()
    tx = builder.create_tx_block()

    try:
        borrowed = tx.borrow_quick(amount, coin_name, obligation_id, obligation_key)
        tx.transfer_objects([borrowed], client.wallet_address)
        result = builder.sign_and_send_tx_block(tx)

        if result.success:
            print(f"Borrowed {amount} {coin_name}: {result.digest}")
        else:
            print(f"TX failed: {result.error}")

        return result

    except InsufficientBalanceError as e:
        print(f"Insufficient balance: need {e.required}, have {e.available}")
    except ObligationLockedError:
        print("Obligation is locked — unstake from borrow incentive first")
    except OraclePriceError as e:
        print(f"Oracle unavailable for {e.coin_name}")
    except NetworkError as e:
        print(f"RPC error: {e.message}")
    except ScallopError as e:
        print(f"Error: {e}")

    return None
```

## References

- [Error Codes](../../references/error-codes.md) - Full error code reference
- [Supported Coins](../../references/supported-coins.md) - Valid coin names

