Error Handling
Handle errors gracefully in your application.
Overview
The SDK can throw various errors during transaction execution. This guide covers common errors and how to handle them.
Error Categories
Insufficient Funds
User doesn't have enough balance:
typescript
try {
await sdk.placeWagerV2({
bet: betAddress,
amount: 1000,
side: Outcome.For,
signers: [wallet],
});
} catch (error) {
if (error.message.includes("insufficient funds") ||
error.message.includes("0x1")) {
console.log("Not enough USDC to place this wager");
// Show deposit UI
}
}Invalid State
Operation not allowed in current bet state:
typescript
try {
await sdk.placeWagerV2({
bet: betAddress,
amount: 25,
side: Outcome.For,
signers: [wallet],
});
} catch (error) {
if (error.message.includes("bet is not pending")) {
console.log("This bet is no longer accepting wagers");
} else if (error.message.includes("already resolving")) {
console.log("Voting has already started");
}
}Account Errors
Missing or invalid accounts:
typescript
try {
await sdk.accounts.betV2.single(invalidAddress);
} catch (error) {
if (error.message.includes("Account does not exist")) {
console.log("Bet not found at this address");
}
}Network Errors
RPC or network issues:
typescript
try {
await sdk.placeWagerV2({...});
} catch (error) {
if (error.message.includes("failed to send transaction") ||
error.message.includes("timeout")) {
console.log("Network error, please try again");
} else if (error.message.includes("blockhash not found")) {
console.log("Transaction expired, please retry");
}
}Comprehensive Error Handler
typescript
interface SDKError {
type: "INSUFFICIENT_FUNDS" | "INVALID_STATE" | "ACCOUNT_NOT_FOUND" |
"ALREADY_EXISTS" | "PERMISSION_DENIED" | "NETWORK" | "UNKNOWN";
message: string;
original: Error;
}
function parseSDKError(error: Error): SDKError {
const message = error.message.toLowerCase();
// Insufficient funds
if (message.includes("insufficient") || message.includes("0x1")) {
return {
type: "INSUFFICIENT_FUNDS",
message: "Not enough balance for this operation",
original: error,
};
}
// Invalid state transitions
if (message.includes("not pending") ||
message.includes("not resolving") ||
message.includes("already resolved")) {
return {
type: "INVALID_STATE",
message: "Operation not allowed in current state",
original: error,
};
}
// Account not found
if (message.includes("account does not exist") ||
message.includes("account not found")) {
return {
type: "ACCOUNT_NOT_FOUND",
message: "The requested account was not found",
original: error,
};
}
// Already exists
if (message.includes("already in use") ||
message.includes("already exists")) {
return {
type: "ALREADY_EXISTS",
message: "This account already exists",
original: error,
};
}
// Permission errors
if (message.includes("not authorized") ||
message.includes("owner mismatch")) {
return {
type: "PERMISSION_DENIED",
message: "You don't have permission for this action",
original: error,
};
}
// Network errors
if (message.includes("timeout") ||
message.includes("failed to send") ||
message.includes("blockhash")) {
return {
type: "NETWORK",
message: "Network error, please try again",
original: error,
};
}
return {
type: "UNKNOWN",
message: error.message,
original: error,
};
}Usage Example
typescript
async function placeWagerSafely(
betAddress: PublicKey,
amount: number,
side: Outcome
) {
try {
const txHash = await sdk.placeWagerV2({
bet: betAddress,
amount,
side,
signers: [wallet],
});
return { success: true, txHash };
} catch (error) {
const parsed = parseSDKError(error as Error);
switch (parsed.type) {
case "INSUFFICIENT_FUNDS":
return { success: false, error: "Please add more USDC" };
case "INVALID_STATE":
return { success: false, error: "Bet is no longer accepting wagers" };
case "NETWORK":
return { success: false, error: "Please check your connection" };
default:
console.error("Unexpected error:", parsed.original);
return { success: false, error: "Something went wrong" };
}
}
}Program Error Codes
Common Anchor program error codes:
| Code | Description |
|---|---|
| 0x0 | Generic error |
| 0x1 | Insufficient funds |
| 0x64 | (100) Account already initialized |
| 0x65 | (101) Invalid state transition |
| 0x66 | (102) Unauthorized |
| 0x67 | (103) Invalid argument |
Validation Before Sending
Validate conditions before sending transactions:
typescript
async function validateWager(
betAddress: PublicKey,
amount: number
): Promise<{ valid: boolean; error?: string }> {
// Check bet status
const bet = await sdk.accounts.betV2.single(betAddress);
if (bet.status !== MarketStatus.Pending) {
return { valid: false, error: "Bet is not accepting wagers" };
}
// Check user balance
const tokenAccount = await getAccount(connection, userTokenAddress);
const balance = Number(tokenAccount.amount) / 1e6;
if (balance < amount) {
return { valid: false, error: `Insufficient balance: ${balance} USDC` };
}
// Check user has program account
try {
await sdk.accounts.user.single(userAddress);
} catch {
return { valid: false, error: "Please create a program user first" };
}
return { valid: true };
}Logging Errors
Implement proper error logging:
typescript
function logError(context: string, error: Error) {
const parsed = parseSDKError(error);
console.error({
context,
type: parsed.type,
message: parsed.message,
stack: error.stack,
timestamp: new Date().toISOString(),
});
// Send to error tracking service
// Sentry.captureException(error, { extra: { context, parsed } });
}