Skip to main content
Proper error handling is crucial for building robust Solana programs. Anchor provides a comprehensive error handling system with custom errors, built-in framework errors, and helpful macros for validation.

Error Types in Anchor

Anchor errors fall into two main categories:
  1. Framework Errors: Built-in errors from Anchor (error codes 100-5000)
  2. Custom Errors: Program-specific errors you define (starting at 6000)

The Error Enum

Anchor’s error type is defined as:
pub enum Error {
    AnchorError(Box<AnchorError>),
    ProgramError(Box<ProgramErrorWithOrigin>),
}

Defining Custom Errors

Use the #[error_code] attribute to define custom errors:
#[error_code]
pub enum ErrorCode {
    #[msg("The provided value is too large")]
    ValueTooLarge,
    #[msg("The provided value is too small")]
    ValueTooSmall,
    #[msg("Unauthorized access attempt")]
    Unauthorized,
    #[msg("Insufficient funds for this operation")]
    InsufficientFunds,
}

Error Code Numbers

Custom errors are automatically assigned codes starting from 6000:
ValueTooLarge = 6000
ValueTooSmall = 6001
Unauthorized = 6002
InsufficientFunds = 6003
Framework error code ranges:
RangeDescription
>= 100Instruction errors
>= 1000IDL errors
>= 2000Constraint errors
>= 3000Account errors
>= 4100Miscellaneous errors
>= 6000Custom user errors

Returning Errors

Using err! Macro

The err! macro returns an error from your instruction:
pub fn set_value(ctx: Context<SetValue>, value: u64) -> Result<()> {
    if value > 100 {
        return err!(ErrorCode::ValueTooLarge);
    }
    
    ctx.accounts.my_account.value = value;
    Ok(())
}

Using require! Macro

The require! macro provides a concise way to validate conditions:
pub fn set_value(ctx: Context<SetValue>, value: u64) -> Result<()> {
    require!(value <= 100, ErrorCode::ValueTooLarge);
    require!(value >= 10, ErrorCode::ValueTooSmall);
    
    ctx.accounts.my_account.value = value;
    Ok(())
}
If the condition is false, require! returns the specified error.

Validation Macros

Anchor provides several macros for common validation patterns:

require!

Ensures a condition is true:
require!(amount > 0, ErrorCode::InvalidAmount);

require_eq!

Ensures two values are equal:
require_eq!(account.owner, authority.key(), ErrorCode::Unauthorized);

require_neq!

Ensures two values are not equal:
require_neq!(from.key(), to.key(), ErrorCode::SameAccount);

require_keys_eq!

Ensures two Pubkeys are equal:
require_keys_eq!(account.authority, authority.key(), ErrorCode::Unauthorized);

require_keys_neq!

Ensures two Pubkeys are not equal:
require_keys_neq!(sender.key(), receiver.key(), ErrorCode::CannotSendToSelf);

require_gt!

Ensures the first value is greater than the second:
require_gt!(new_amount, old_amount, ErrorCode::AmountNotIncreased);

require_gte!

Ensures the first value is greater than or equal to the second:
require_gte!(balance, amount, ErrorCode::InsufficientFunds);

Complete Example

Here’s a comprehensive example demonstrating error handling:
use anchor_lang::prelude::*;

declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");

#[program]
pub mod vault {
    use super::*;

    pub fn initialize(ctx: Context<Initialize>, max_amount: u64) -> Result<()> {
        require!(max_amount > 0, ErrorCode::InvalidMaxAmount);
        
        let vault = &mut ctx.accounts.vault;
        vault.authority = ctx.accounts.authority.key();
        vault.balance = 0;
        vault.max_amount = max_amount;
        
        msg!("Vault initialized with max amount: {}", max_amount);
        Ok(())
    }

    pub fn deposit(ctx: Context<Deposit>, amount: u64) -> Result<()> {
        require!(amount > 0, ErrorCode::InvalidAmount);
        
        let vault = &mut ctx.accounts.vault;
        let new_balance = vault.balance
            .checked_add(amount)
            .ok_or(ErrorCode::Overflow)?;
        
        require!(new_balance <= vault.max_amount, ErrorCode::ExceedsMaxAmount);
        
        vault.balance = new_balance;
        msg!("Deposited {}. New balance: {}", amount, vault.balance);
        Ok(())
    }

    pub fn withdraw(
        ctx: Context<Withdraw>,
        amount: u64,
    ) -> Result<()> {
        require!(amount > 0, ErrorCode::InvalidAmount);
        require_gte!(ctx.accounts.vault.balance, amount, ErrorCode::InsufficientFunds);
        
        let vault = &mut ctx.accounts.vault;
        vault.balance = vault.balance
            .checked_sub(amount)
            .ok_or(ErrorCode::Underflow)?;
        
        msg!("Withdrew {}. Remaining balance: {}", amount, vault.balance);
        Ok(())
    }
}

#[derive(Accounts)]
pub struct Initialize<'info> {
    #[account(
        init,
        payer = authority,
        space = 8 + Vault::INIT_SPACE
    )]
    pub vault: Account<'info, Vault>,
    #[account(mut)]
    pub authority: Signer<'info>,
    pub system_program: Program<'info, System>,
}

#[derive(Accounts)]
pub struct Deposit<'info> {
    #[account(mut)]
    pub vault: Account<'info, Vault>,
}

#[derive(Accounts)]
pub struct Withdraw<'info> {
    #[account(
        mut,
        has_one = authority @ ErrorCode::Unauthorized
    )]
    pub vault: Account<'info, Vault>,
    pub authority: Signer<'info>,
}

#[account]
#[derive(InitSpace)]
pub struct Vault {
    pub authority: Pubkey,
    pub balance: u64,
    pub max_amount: u64,
}

#[error_code]
pub enum ErrorCode {
    #[msg("Amount must be greater than zero")]
    InvalidAmount,
    #[msg("Max amount must be greater than zero")]
    InvalidMaxAmount,
    #[msg("Insufficient funds in vault")]
    InsufficientFunds,
    #[msg("Deposit would exceed maximum vault amount")]
    ExceedsMaxAmount,
    #[msg("Only the authority can perform this action")]
    Unauthorized,
    #[msg("Arithmetic overflow occurred")]
    Overflow,
    #[msg("Arithmetic underflow occurred")]
    Underflow,
}

Error Responses

When an error occurs, Anchor provides detailed information in the transaction logs:
Program log: AnchorError thrown in programs/vault/src/lib.rs:25. 
  Error Code: InsufficientFunds. 
  Error Number: 6002. 
  Error Message: Insufficient funds in vault.

Client-Side Error Handling

In TypeScript clients, catch and handle errors:
try {
  await program.methods
    .withdraw(new anchor.BN(1000))
    .accounts({ vault, authority })
    .rpc();
} catch (error) {
  if (error.error.errorCode.code === 'InsufficientFunds') {
    console.log('Not enough funds in vault');
    console.log('Error message:', error.error.errorMessage);
  }
}
Error response structure:
{
  error: {
    errorCode: { code: 'InsufficientFunds', number: 6002 },
    errorMessage: 'Insufficient funds in vault',
    origin: { file: 'programs/vault/src/lib.rs', line: 25 }
  },
  logs: [...],
  errorLogs: [...]
}

Common Framework Errors

Some frequently encountered Anchor framework errors:
ErrorCodeDescription
ConstraintMut2000Account should be mutable
ConstraintHasOne2001has_one constraint violated
ConstraintSigner2002Account should be a signer
ConstraintOwner2004Wrong account owner
ConstraintSeeds2006PDA seeds don’t match
AccountNotInitialized3012Account not initialized
RequireViolated2500require! condition failed

Best Practices

  1. Use descriptive error messages: Make errors easy to understand and debug
  2. Validate early: Check conditions at the start of instructions
  3. Use specific errors: Create distinct error codes for different failure cases
  4. Leverage constraints: Use account constraints for validation when possible
  5. Check arithmetic: Use checked operations to prevent overflows
  6. Document errors: Add comments explaining when each error is used
  7. Test error cases: Write tests that verify error conditions

Using Constraints for Validation

Many validations can be done with account constraints instead of manual checks:
#[derive(Accounts)]
pub struct Withdraw<'info> {
    #[account(
        mut,
        has_one = authority @ ErrorCode::Unauthorized,
        constraint = vault.balance >= amount @ ErrorCode::InsufficientFunds
    )]
    pub vault: Account<'info, Vault>,
    pub authority: Signer<'info>,
}
This approach is more concise and errors are caught during account validation.

Build docs developers (and LLMs) love