#[error_code] & require!
Anchor #[error_code] enums map to stable error codes in the IDL. require! and friends assert conditions with those errors.
Recipe
#[error_code]
pub enum MyError {
#[msg("Amount must be positive")]
InvalidAmount,
}
require!(amount > 0, MyError::InvalidAmount);
require_eq!(owner, ctx.accounts.authority.key(), MyError::Unauthorized);When to reach for this: You need typed, client-decodable failures.
Working Example
#[error_code]
pub enum VaultError {
#[msg("Vault is paused")]
Paused,
#[msg("Insufficient balance")]
InsufficientFunds,
#[msg("Math overflow")]
Overflow,
}
pub fn withdraw(ctx: Context<Withdraw>, amount: u64) -> Result<()> {
require!(!ctx.accounts.vault.paused, VaultError::Paused);
require!(ctx.accounts.vault.balance >= amount, VaultError::InsufficientFunds);
ctx.accounts.vault.balance = ctx.accounts.vault.balance
.checked_sub(amount)
.ok_or(VaultError::Overflow)?;
Ok(())
}What this demonstrates:
- Errors appear in IDL for clients
- #[msg] sets human-readable text
- require_eq! compares values
- Use Result? for CPI errors mapping
Deep Dive
Macros
| Macro | Use |
|---|---|
require! | Boolean condition |
require_eq! / require_ne! | Equality checks |
require_keys_eq! | Pubkey comparisons |
Anchor assigns error codes starting at custom offset; clients decode via IDL.
Gotchas
- Raw ProgramError::Custom - Clients lose IDL mapping.. Fix: Use #[error_code].
- Duplicate error messages - Support confusion.. Fix: One variant per distinct failure.
- require in tight loops - CU cost adds up.. Fix: Validate once before loop.
- Leaking internal state in msg - Security info leak.. Fix: Keep messages user-safe.
- Forgetting ? on CPI - Error swallowed.. Fix: Propagate with ? or map_err.
Alternatives
| Alternative | Use When | Don't Use When |
|---|---|---|
| constraint @ Error | Account validation time | Handler business rules |
| Native custom errors | Non-Anchor | IDL-integrated clients |
FAQs
Anchor 0.32.1 error format?
Custom codes in IDL plus #[msg] strings.
emit! vs emit_cpi!?
emit_cpi! costs more CU but improves capture reliability.
Should I use msg! on mainnet?
Avoid except behind feature flags.
How do clients get error codes?
From IDL via Anchor or Codama codegen.
Do events appear in IDL?
Yes, #[event] types are listed.
Can constraints return custom errors?
Yes with @ MyError::Variant.
What uses CU in logging?
Each log line and formatted string.
How to test errors?
anchor test expecting error codes.
Are logs consensus data?
Yes, but not an API contract; use events.
Next read?
See Related for errors.
Related
- Custom Error Messages - messages
- Client-Side Error Decoding - TS decode
- Program Errors Basics - native errors
Stack versions: This page was written for Agave 4.1.1, Solana CLI 3.0.10, Anchor 0.32.1, anchor-lang 0.32.1, Rust 1.91.1, @solana/kit 7.0.0, Surfpool 0.12.0, and LiteSVM 0.6.x.