The #[account] Constraints
Field attributes on accounts struct members compose validation rules. Anchor evaluates them in a defined order before your instruction handler runs.
Recipe
#[account(
mut,
has_one = authority,
constraint = amount > 0 @ MyError::InvalidAmount,
)]
pub vault: Account<'info, Vault>,When to reach for this: You express ownership, relationships, or custom invariants declaratively.
Working Example
#[derive(Accounts)]
pub struct SetFee<'info> {
#[account(
mut,
seeds = [b"config"],
bump = config.bump,
has_one = admin,
constraint = new_fee_bps <= 10_000 @ ConfigError::FeeTooHigh,
)]
pub config: Account<'info, Config>,
pub admin: Signer<'info>,
}What this demonstrates:
mutmarks writable accountshas_onechecks pubkey equality to nested fieldconstraintruns arbitrary boolean expressions- Error after
@maps to custom error enum variant
Deep Dive
Common Attributes
| Attribute | Purpose |
|---|---|
mut | Account is writable |
signer | Must be signer (on non-Signer types) |
has_one = field | Account field equals other account key |
address = expr | Exact pubkey match |
owner = prog | Owner program check |
constraint = expr | Custom invariant |
Combine attributes; all must pass.
Gotchas
- constraint without @ error - Generic constraint violation message.. Fix: Add
@ MyError::Variantfor clarity. - has_one typo - Silent failure to compile or wrong field.. Fix: Field name must exist on account struct.
- address = wrong key - Bricks mainnet if hardcoded test key.. Fix: Use
pubkey!constants per cluster feature flags. - mut missing on has_one target - Read-only when you need write.. Fix: Add
muton accounts you persist. - Overlapping constraint and handler check - Duplicated logic drifts.. Fix: Pick declarative or handler, not both for same rule.
Alternatives
| Alternative | Use When | Don't Use When |
|---|---|---|
| require! in handler | Dynamic logic across accounts | Static relationship checks |
| Custom constraint tokens (0.32) | Reusable patterns | One-off simple rules |
| Manual native checks | No Anchor | You use Anchor for safety |
FAQs
What Anchor version does this site use?
0.32.1 for anchor-lang, Anchor CLI, and examples in this section.
Do I need Solana CLI alongside Anchor?
Yes. Solana CLI 3.0.10 handles keypairs, airdrops, and solana program inspection.
Where does the IDL live after build?
target/idl/<program>.json in your workspace.
Can I mix UncheckedAccount with Signer?
Yes, but every unchecked field needs explicit constraints or handler checks.
How do I test without devnet?
Use anchor test with Surfpool 0.12.0 or LiteSVM 0.6.x in CI.
What is the 8-byte prefix on account data?
Anchor account discriminator; do not strip it when sizing space.
Should I commit generated IDL?
Yes, or publish on-chain IDL so clients have a canonical source.
How do I debug constraint failures?
Run with logs; Anchor prints constraint name and account index.
Does Anchor work on Agave 4.1.1?
Yes. This stack targets Agave validators with Solana CLI 3.0.10.
What should I read next in this section?
See sibling articles linked in Related for deeper account constraints topics.
Related
- Custom Constraints - advanced expressions
- Account Validation Order - evaluation order
- init & init_if_needed - lifecycle constraints
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.