Problem
#[error_code] enums currently have no way to attach human-readable messages to error variants. Anchor solves this with #[msg("...")] attributes, but that approach embeds string literals into the on-chain binary — bloating program size for zero runtime benefit. Error messages are never read on-chain; they only matter for debugging in explorers and test output.
Proposal
Support an optional #[msg("...")] attribute on #[error_code] variants, but strip the strings at compile time so they never appear in the binary. Instead, emit them exclusively into the IDL:
#[error_code]
pub enum VaultError {
#[msg("signer does not match vault authority")]
Unauthorized,
#[msg("insufficient lamports for withdrawal")]
InsufficientFunds,
}
On-chain, this compiles to exactly what we have today — ProgramError::Custom(N) with no string data. The IDL gains an errors section that maps discriminants to names and messages:
{
"errors": [
{ "code": 6000, "name": "Unauthorized", "msg": "signer does not match vault authority" },
{ "code": 6001, "name": "InsufficientFunds", "msg": "insufficient lamports for withdrawal" }
]
}
Integration
- Explorers: Read the IDL errors section to display descriptive messages for failed transactions instead of raw
Custom(6000)
- QuasarSVM / test frameworks: Resolve
ProgramError::Custom codes against the IDL to show the message in test failure output
- CLI (
quasar idl): Already generates the IDL — extend it to include the errors section
Why this is better than Anchor's approach
Anchor bakes #[msg] strings into the compiled binary via std::fmt::Display. This costs binary size on every program deploy for strings that are never used on-chain. By keeping messages IDL-only, we get the same developer experience with zero binary overhead — programs stay as small as possible.
Scope
quasar-derive: Parse #[msg("...")] on #[error_code] variants, discard the strings during macro expansion (no codegen change)
quasar idl: Extract #[msg] strings and emit them in the IDL errors array
- QuasarSVM test runner: Resolve error codes against the IDL for readable test output
- CLI templates: Update
template add-error to show #[msg] in the generated scaffold
Problem
#[error_code]enums currently have no way to attach human-readable messages to error variants. Anchor solves this with#[msg("...")]attributes, but that approach embeds string literals into the on-chain binary — bloating program size for zero runtime benefit. Error messages are never read on-chain; they only matter for debugging in explorers and test output.Proposal
Support an optional
#[msg("...")]attribute on#[error_code]variants, but strip the strings at compile time so they never appear in the binary. Instead, emit them exclusively into the IDL:On-chain, this compiles to exactly what we have today —
ProgramError::Custom(N)with no string data. The IDL gains anerrorssection that maps discriminants to names and messages:{ "errors": [ { "code": 6000, "name": "Unauthorized", "msg": "signer does not match vault authority" }, { "code": 6001, "name": "InsufficientFunds", "msg": "insufficient lamports for withdrawal" } ] }Integration
Custom(6000)ProgramError::Customcodes against the IDL to show the message in test failure outputquasar idl): Already generates the IDL — extend it to include the errors sectionWhy this is better than Anchor's approach
Anchor bakes
#[msg]strings into the compiled binary viastd::fmt::Display. This costs binary size on every program deploy for strings that are never used on-chain. By keeping messages IDL-only, we get the same developer experience with zero binary overhead — programs stay as small as possible.Scope
quasar-derive: Parse#[msg("...")]on#[error_code]variants, discard the strings during macro expansion (no codegen change)quasar idl: Extract#[msg]strings and emit them in the IDLerrorsarraytemplate add-errorto show#[msg]in the generated scaffold