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
# 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)
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.
try:
config = ScallopConfig(network="invalid")
except ScallopConfigError as e:
print(f"Config error: {e}")
ObligationNotFoundError
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.
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
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
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
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
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
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
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:
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.
Error Handling Patterns
Broad Catch
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
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
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 - Full error code reference
- Supported Coins - Valid coin names