init & init_if_needed
init creates a new account in the same instruction. init_if_needed creates only when missing, which introduces reinitialization risk if constraints are weak.
Search across all documentation pages
init creates a new account in the same instruction. init_if_needed creates only when missing, which introduces reinitialization risk if constraints are weak.
#[account(
init,
payer = payer,
space = 8 + MyAccount::INIT_SPACE,
seeds = [b"vault", user.key().as_ref()],
bump
)]
pub vault: Account<'info, Vault>,When to reach for this: An instruction must allocate program-owned state for the first time.
#[derive(Accounts)]
pub struct CreateProfile<'info> {
#[account(
init,
payer = user,
space = 8 + Profile::INIT_SPACE,
seeds = [b"profile", user.key().as_ref()],
bump
)]
pub profile: Account<'info, Profile>,
#[account(mut)]
pub user: Signer<'info>,
pub system_program: Program<'info, System>,
}init_if_needed is gated behind a cargo feature. Without it the macro fails to build with an error telling you to enable it:
[dependencies]
anchor-lang = { version = "0.32.1", features = ["init-if-needed"] }// init_if_needed: only when idempotent and safe
#[account(
init_if_needed,
payer = payer,
space = 8 + Stats::INIT_SPACE,
seeds = [b"stats"],
bump,
)]
pub stats: Account<'info, Stats>,What this demonstrates:
init fails if account already existspayer funds rent-exempt lamportsspace includes 8-byte discriminatorinit_if_needed can skip creation when account existsinit_if_needed never re-creates an existing account, so your handler body still executes against live state. Anchor does run the full Account<T> path on the existing account (owner + 8-byte discriminator + Borsh) and re-validates the declared attributes such as space, owner, and token::authority even when creation is skipped, so arbitrary garbage is rejected.
The real reinitialization attack is different: if the handler body unconditionally writes defaults (ctx.accounts.stats.owner = signer.key();), a second call resets a legitimate, already-populated account. Guard with a stored initialized: bool / version field, or constraint = stats.authority == authority.key(), or split create and update into two instructions.
INIT_SPACE or space = 8 + ....init or strict constraint on existing state.Program<System>.#[account(mut)].init_if_needed only with strong guards.
| Alternative | Use When | Don't Use When |
|---|---|---|
| Separate create instruction | Clear lifecycle | One-shot setup UX |
| CPI to system program manually | Native programs | Anchor init macros |
| init_if_needed + constraint | Idempotent onboarding | High-security vaults |
0.32.1 for anchor-lang, Anchor CLI, and examples in this section.
Yes. Solana CLI 3.0.10 handles keypairs, airdrops, and solana program inspection.
target/idl/<program>.json in your workspace.
Yes, but every unchecked field needs explicit constraints or handler checks.
Use anchor test with Surfpool 0.12.0 or LiteSVM 0.6.x in CI.
Anchor account discriminator; do not strip it when sizing space.
Yes, or publish on-chain IDL so clients have a canonical source.
Run with logs; Anchor prints constraint name and account index.
Yes. This stack targets Agave validators with Solana CLI 3.0.10.
See sibling articles linked in Related for deeper init topics.
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.
Reviewed by Chris St. John·Last updated Jul 19, 2026