Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/blueshift-gg/quasar/llms.txt

Use this file to discover all available pages before exploring further.

Quasar programs express failures through typed error enums that convert to ProgramError::Custom(N) at the ABI boundary. The framework reserves error codes 3000–5999 for built-in QuasarError variants and leaves 6000 and above for program-specific errors defined with #[error_code].

#[error_code] — Program-Specific Errors

Annotate an enum with #[error_code] to generate From<MyError> for ProgramError and TryFrom<u32> for MyError conversions. Variants are numbered starting from their explicit discriminant (or continuing from the previous one):
use quasar_lang::prelude::*;

#[error_code]
pub enum MyError {
    Unauthorized = 6000,   // → ProgramError::Custom(6000)
    InvalidAmount,         // → ProgramError::Custom(6001)
    Overflow,              // → ProgramError::Custom(6002)
}
The conventional starting value is 6000 (Anchor-compatible). Without an explicit discriminant the macro starts at 0, so always set it explicitly on the first variant:
#[error_code]
pub enum VaultError {
    Unauthorized = 6000,
    DepositTooSmall,       // 6001
    WithdrawExceedsBalance, // 6002
}
Return a program error from any handler simply by using .into() or the ? operator on an expression that produces Result<_, MyError>:
return Err(MyError::Unauthorized.into());

require!, require_eq!, require_keys_eq!

These macros provide concise early-return assertions. They evaluate to nothing on success and return an error on failure:
// require! — boolean condition
require!(ctx.accounts.counter.authority == *ctx.accounts.signer.address(),
         MyError::Unauthorized);

// require_eq! — equality check (PartialEq)
require_eq!(deposit_amount, expected_amount, MyError::InvalidAmount);

// require_keys_eq! — address equality (four u64 word comparison, ~10 CU)
require_keys_eq!(
    *escrow.maker,
    *maker.address(),
    MyError::Unauthorized
);
All three macros expand to an if !condition { return Err(error.into()); } expression and are valid in any function returning Result<_, ProgramError>.

Built-in QuasarError Variants

QuasarError is defined in quasar_lang::error and covers every constraint and validation failure the framework can produce. All variants start at code 3000:
VariantCodeWhen emitted
AccountNotInitialized3000Account data is all zeros (discriminator check fails before any field access)
AccountAlreadyInitialized3001#[account(init)] on an account already owned by the program
InvalidPda3002PDA derivation does not match the expected address
InvalidSeeds3003Seeds produce an on-curve point (not a valid PDA) or seed count exceeds 16
ConstraintViolation3004#[account(constraints(...))] expression evaluated to false
HasOneMismatch3005#[account(has_one(...))] field doesn’t match the other account’s address
InvalidDiscriminator3006Account discriminator bytes don’t match the expected value for the type
InsufficientSpace3007Account data is too short for the declared Space
AccountNotRentExempt3008Lamport balance is below the rent-exemption minimum
AccountOwnedByWrongProgram3009Account owner doesn’t match the expected program
AccountNotMutable3010#[account(mut)] on an account not passed as writable
AccountNotSigner3011Signer account not passed with the signer flag set
AddressMismatch3012#[account(address = ...)] expression doesn’t match the account’s key
DynamicFieldTooLong3013A PodString or PodVec field exceeds its declared maximum length
CompactWriterFieldNotSet3014set_inner committed before every compact field was written
RemainingAccountsOverflow3015More remaining accounts than the iterator buffer can hold
RemainingAccountDuplicate3016A duplicate remaining-account entry could not be resolved
MissingReturnData3017CPI completed without setting return data (invoke_with_return path)
ReturnDataFromWrongProgram3018Return data was set by a different program than the one invoked
InvalidReturnData3019Return data length doesn’t match the expected fixed-size layout
These are emitted automatically by the framework during account parsing and constraint evaluation. You do not need to handle them in your program logic unless you want to wrap or rethrow them.

ProgramError vs QuasarError

Both QuasarError and user #[error_code] enums convert to ProgramError::Custom(N) via From. The reverse conversion (TryFrom<u32>) is also generated so that client code can recover the typed variant from a transaction error:
// On-chain: typed → ProgramError
let err: ProgramError = MyError::Unauthorized.into();
// err == ProgramError::Custom(6000)

// Off-chain client: u32 → typed
let my_err = MyError::try_from(6000).unwrap();
// my_err == MyError::Unauthorized
Solana’s built-in ProgramError variants (not Custom) occupy codes 0–29. Quasar’s QuasarError range starts at 3000, leaving codes 30–2999 unused. User #[error_code] enums conventionally start at 6000 to match Anchor’s offset and leave room if the framework adds more built-in errors.

Code range 0–29

Solana built-in ProgramError variants (e.g. MissingRequiredSignature, InsufficientFunds, AccountBorrowFailed)

Code range 3000–5999

Quasar built-in QuasarError variants — framework constraint failures

Code range 6000+

User-defined #[error_code] variants — program-specific business logic errors

Convention

Start your #[error_code] enum at 6000 explicitly. Do not overlap with the 3000–5999 range reserved for Quasar.

Complete Error Example

A typical program defines its errors in a dedicated errors.rs module and imports it in lib.rs:
// src/errors.rs
use quasar_lang::prelude::*;

#[error_code]
pub enum EscrowError {
    /// The offered amount is zero.
    ZeroDeposit = 6000,
    /// The requested receive amount is zero.
    ZeroReceive,
    /// The maker's token account has insufficient balance.
    InsufficientBalance,
}
// src/instructions/make.rs
use crate::errors::EscrowError;
use quasar_lang::prelude::*;

impl Make {
    pub fn validate_amounts(&self, deposit: u64, receive: u64) -> Result<(), ProgramError> {
        require!(deposit > 0, EscrowError::ZeroDeposit);
        require!(receive > 0, EscrowError::ZeroReceive);
        require!(
            self.maker_ta_a.amount() >= deposit,
            EscrowError::InsufficientBalance
        );
        Ok(())
    }
}
Doc comments on #[error_code] variants are captured in the program’s IDL. Add /// comments to every variant so that client SDK consumers get human-readable error messages without having to look up numeric codes.

Build docs developers (and LLMs) love