Error Handling

Classify, parse, and recover from errors in your SDK integration.

Error categories

SDK errors fall into five categories. Match on error message strings to classify:

Insufficient funds

User doesn't have enough USDC balance.

typescript
try {
  await sdk.placeWagerV2({ bet, amount: 1000, side: Outcome.For, signers });
} catch (error) {
  if ((error as Error).message.includes("insufficient funds") ||
      (error as Error).message.includes("0x1")) {
    console.log("Not enough USDC to place this wager");
  }
}

Invalid state

Operation not allowed in current bet state (e.g. wager on a resolved bet).

typescript
} catch (error) {
  if ((error as Error).message.includes("not pending")) {
    console.log("Bet is no longer accepting wagers");
  } else if ((error as Error).message.includes("already resolving")) {
    console.log("Voting has already started");
  }
}

Account not found

The requested on-chain account doesn't exist.

typescript
try {
  await sdk.accounts.betV2.single(invalidAddress);
} catch (error) {
  if ((error as Error).message.includes("Account does not exist")) {
    console.log("Bet not found at this address");
  }
}

Network errors

RPC timeouts or blockhash expiry — these are typically retryable.

typescript
} catch (error) {
  if ((error as Error).message.includes("blockhash not found")) {
    console.log("Transaction expired, please retry");
  } else if ((error as Error).message.includes("timeout")) {
    console.log("Network error, please try again");
  }
}

Error parser

Build a reusable parser to classify errors and generate user-friendly messages:

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 msg = error.message.toLowerCase();

  if (msg.includes("insufficient") || msg.includes("0x1")) {
    return { type: "INSUFFICIENT_FUNDS", message: "Not enough balance", original: error };
  }
  if (msg.includes("not pending") || msg.includes("already resolved")) {
    return { type: "INVALID_STATE", message: "Operation not allowed in current state", original: error };
  }
  if (msg.includes("account does not exist")) {
    return { type: "ACCOUNT_NOT_FOUND", message: "Account not found", original: error };
  }
  if (msg.includes("already in use")) {
    return { type: "ALREADY_EXISTS", message: "Account already exists", original: error };
  }
  if (msg.includes("not authorized") || msg.includes("owner mismatch")) {
    return { type: "PERMISSION_DENIED", message: "Permission denied", original: error };
  }
  if (msg.includes("timeout") || msg.includes("blockhash") || msg.includes("failed to send")) {
    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 and retry" };
      default:
        console.error("Unexpected error:", parsed.original);
        return { success: false, error: "Something went wrong" };
    }
  }
}

Program error codes

Common Anchor error codes that appear in error messages:

CodeDescription
0x0Generic error
0x1Insufficient funds
0x64 (100)Account already initialized
0x65 (101)Invalid state transition
0x66 (102)Unauthorized
0x67 (103)Invalid argument

Pre-send validation

Validate conditions before sending transactions to surface errors earlier and avoid wasted gas:

typescript
async function validateWager(
  betAddress: PublicKey,
  amount: number
): Promise<{ valid: boolean; error?: string }> {
  // Check bet state
  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 a program account
  try {
    await sdk.accounts.user.single(sdk.addresses.user.get(wallet.publicKey));
  } catch {
    return { valid: false, error: "Please create a program user first" };
  }

  return { valid: true };
}