Scallop error handling
Skill scallop-io/scallop-skills/skills/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.From its SKILL.md
npx -y skills add scallop-io/scallop-skills --skill scallop-error-handlingAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its file declares
Copied from the file, not written here
The file declares its own license as MIT. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.
SKILL.md
7.8 KB, as published. Nobody here has run it
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