Skip to main content

anchor_lang_error/
lib.rs

1use {
2    crate::compat::solana_pubkey::Pubkey,
3    anchor_attribute_error::error_code,
4    solana_program_error::ProgramError,
5    std::{
6        fmt::{Debug, Display},
7        num::TryFromIntError,
8    },
9};
10
11/// Aliases the active Solana cohort back to its unsuffixed crate name.
12mod compat {
13    #[cfg(all(feature = "solana-v3", feature = "solana-v4"))]
14    compile_error!(
15        "anchor-lang-error: features `solana-v3` and `solana-v4` are mutually exclusive. A \
16         dependency most likely re-enabled default features; pass `default-features = false` \
17         there, or pick a single cohort."
18    );
19    #[cfg(not(any(feature = "solana-v3", feature = "solana-v4")))]
20    compile_error!(
21        "anchor-lang-error: enable exactly one of the `solana-v3` or `solana-v4` features."
22    );
23
24    #[cfg(feature = "solana-v3")]
25    pub(crate) use solana_pubkey_v3 as solana_pubkey;
26    #[cfg(feature = "solana-v4")]
27    pub(crate) use solana_pubkey_v4 as solana_pubkey;
28}
29
30/// The starting point for user defined error codes.
31pub const ERROR_CODE_OFFSET: u32 = 6000;
32
33// `#[error_code]` macro below accesses `anchor_lang::error::...` so set up
34// a fake module for it.
35mod anchor_lang {
36    pub(crate) mod error {
37        pub(crate) use crate::{AnchorError, Error};
38    }
39}
40
41/// Error codes that can be returned by internal framework code.
42///
43/// - >= 100 Instruction error codes
44/// - >= 1000 IDL error codes
45/// - >= 2000 constraint error codes
46/// - >= 3000 account error codes
47/// - >= 4100 misc error codes
48/// - = 5000 deprecated error code
49///
50/// The starting point for user-defined errors is defined
51/// by the [ERROR_CODE_OFFSET](crate::error::ERROR_CODE_OFFSET).
52#[error_code(offset = 0)]
53pub enum ErrorCode {
54    // Instructions
55    /// 100 - Instruction discriminator not provided
56    #[msg("Instruction discriminator not provided")]
57    InstructionMissing = 100,
58    /// 101 - Fallback functions are not supported
59    #[msg("Fallback functions are not supported")]
60    InstructionFallbackNotFound,
61    /// 102 - The program could not deserialize the given instruction
62    #[msg("The program could not deserialize the given instruction")]
63    InstructionDidNotDeserialize,
64    /// 103 - The program could not serialize the given instruction
65    #[msg("The program could not serialize the given instruction")]
66    InstructionDidNotSerialize,
67
68    // IDL instructions (deprecated — use Program Metadata instead)
69    /// 1000 - The program was compiled without idl instructions
70    #[msg("The program was compiled without idl instructions")]
71    IdlInstructionStub = 1000,
72    /// 1001 - Invalid program given to the IDL instruction
73    #[msg("Invalid program given to the IDL instruction")]
74    IdlInstructionInvalidProgram,
75    /// 1002 - IDL Account must be empty in order to resize
76    #[msg("IDL account must be empty in order to resize, try closing first")]
77    IdlAccountNotEmpty,
78
79    // Event instructions
80    /// 1500 - The program was compiled without `event-cpi` feature
81    #[msg("The program was compiled without `event-cpi` feature")]
82    EventInstructionStub = 1500,
83
84    // Constraints
85    /// 2000 - A mut constraint was violated
86    #[msg("A mut constraint was violated")]
87    ConstraintMut = 2000,
88    /// 2001 - A has one constraint was violated
89    #[msg("A has one constraint was violated")]
90    ConstraintHasOne,
91    /// 2002 - A signer constraint was violated
92    #[msg("A signer constraint was violated")]
93    ConstraintSigner,
94    /// 2003 - A raw constraint was violated
95    #[msg("A raw constraint was violated")]
96    ConstraintRaw,
97    /// 2004 - An owner constraint was violated
98    #[msg("An owner constraint was violated")]
99    ConstraintOwner,
100    /// 2005 - A rent exemption constraint was violated
101    #[msg("A rent exemption constraint was violated")]
102    ConstraintRentExempt,
103    /// 2006 - A seeds constraint was violated
104    #[msg("A seeds constraint was violated")]
105    ConstraintSeeds,
106    /// 2007 - An executable constraint was violated
107    #[msg("An executable constraint was violated")]
108    ConstraintExecutable,
109    /// 2008 - Deprecated Error, feel free to replace with something else
110    #[msg("Deprecated Error, feel free to replace with something else")]
111    ConstraintState,
112    /// 2009 - An associated constraint was violated
113    #[msg("An associated constraint was violated")]
114    ConstraintAssociated,
115    /// 2010 - An associated init constraint was violated
116    #[msg("An associated init constraint was violated")]
117    ConstraintAssociatedInit,
118    /// 2011 - A close constraint was violated
119    #[msg("A close constraint was violated")]
120    ConstraintClose,
121    /// 2012 - An address constraint was violated
122    #[msg("An address constraint was violated")]
123    ConstraintAddress,
124    /// 2013 - Expected zero account discriminant
125    #[msg("Expected zero account discriminant")]
126    ConstraintZero,
127    /// 2014 - A token mint constraint was violated
128    #[msg("A token mint constraint was violated")]
129    ConstraintTokenMint,
130    /// 2015 - A token owner constraint was violated
131    #[msg("A token owner constraint was violated")]
132    ConstraintTokenOwner,
133    /// The mint mint is intentional -> a mint authority for the mint.
134    ///
135    /// 2016 - A mint mint authority constraint was violated
136    #[msg("A mint mint authority constraint was violated")]
137    ConstraintMintMintAuthority,
138    /// 2017 - A mint freeze authority constraint was violated
139    #[msg("A mint freeze authority constraint was violated")]
140    ConstraintMintFreezeAuthority,
141    /// 2018 - A mint decimals constraint was violated
142    #[msg("A mint decimals constraint was violated")]
143    ConstraintMintDecimals,
144    /// 2019 - A space constraint was violated
145    #[msg("A space constraint was violated")]
146    ConstraintSpace,
147    /// 2020 - A required account for the constraint is None
148    #[msg("A required account for the constraint is None")]
149    ConstraintAccountIsNone,
150    /// The token token is intentional -> a token program for the token account.
151    ///
152    /// 2021 - A token account token program constraint was violated
153    #[msg("A token account token program constraint was violated")]
154    ConstraintTokenTokenProgram,
155    /// 2022 - A mint token program constraint was violated
156    #[msg("A mint token program constraint was violated")]
157    ConstraintMintTokenProgram,
158    /// 2023 - A mint token program constraint was violated
159    #[msg("An associated token account token program constraint was violated")]
160    ConstraintAssociatedTokenTokenProgram,
161    /// Extension constraints
162    ///
163    /// 2024 - A group pointer extension constraint was violated
164    #[msg("A group pointer extension constraint was violated")]
165    ConstraintMintGroupPointerExtension,
166    /// 2025 - A group pointer extension authority constraint was violated
167    #[msg("A group pointer extension authority constraint was violated")]
168    ConstraintMintGroupPointerExtensionAuthority,
169    /// 2026 - A group pointer extension group address constraint was violated
170    #[msg("A group pointer extension group address constraint was violated")]
171    ConstraintMintGroupPointerExtensionGroupAddress,
172    /// 2027 - A group member pointer extension constraint was violated
173    #[msg("A group member pointer extension constraint was violated")]
174    ConstraintMintGroupMemberPointerExtension,
175    /// 2028 - A group member pointer extension authority constraint was violated
176    #[msg("A group member pointer extension authority constraint was violated")]
177    ConstraintMintGroupMemberPointerExtensionAuthority,
178    /// 2029 - A group member pointer extension member address constraint was violated
179    #[msg("A group member pointer extension group address constraint was violated")]
180    ConstraintMintGroupMemberPointerExtensionMemberAddress,
181    /// 2030 - A metadata pointer extension constraint was violated
182    #[msg("A metadata pointer extension constraint was violated")]
183    ConstraintMintMetadataPointerExtension,
184    /// 2031 - A metadata pointer extension authority constraint was violated
185    #[msg("A metadata pointer extension authority constraint was violated")]
186    ConstraintMintMetadataPointerExtensionAuthority,
187    /// 2032 - A metadata pointer extension metadata address constraint was violated
188    #[msg("A metadata pointer extension metadata address constraint was violated")]
189    ConstraintMintMetadataPointerExtensionMetadataAddress,
190    /// 2033 - A close authority extension constraint was violated
191    #[msg("A close authority constraint was violated")]
192    ConstraintMintCloseAuthorityExtension,
193    /// 2034 - A close authority extension authority constraint was violated
194    #[msg("A close authority extension authority constraint was violated")]
195    ConstraintMintCloseAuthorityExtensionAuthority,
196    /// 2035 - A permanent delegate extension constraint was violated
197    #[msg("A permanent delegate extension constraint was violated")]
198    ConstraintMintPermanentDelegateExtension,
199    /// 2036 - A permanent delegate extension authority constraint was violated
200    #[msg("A permanent delegate extension delegate constraint was violated")]
201    ConstraintMintPermanentDelegateExtensionDelegate,
202    /// 2037 - A transfer hook extension constraint was violated
203    #[msg("A transfer hook extension constraint was violated")]
204    ConstraintMintTransferHookExtension,
205    /// 2038 - A transfer hook extension authority constraint was violated
206    #[msg("A transfer hook extension authority constraint was violated")]
207    ConstraintMintTransferHookExtensionAuthority,
208    /// 2039 - A transfer hook extension transfer hook program id constraint was violated
209    #[msg("A transfer hook extension transfer hook program id constraint was violated")]
210    ConstraintMintTransferHookExtensionProgramId,
211    /// 2040 - A duplicate mutable account constraint was violated
212    #[msg("A duplicate mutable account constraint was violated")]
213    ConstraintDuplicateMutableAccount,
214
215    // Migration errors
216    /// 2041 - Account is already migrated
217    #[msg("Account is already migrated")]
218    AccountAlreadyMigrated,
219    /// 2042 - Account must be migrated before exiting
220    #[msg("Account must be migrated before exiting")]
221    AccountNotMigrated,
222
223    /// 2043 - A pausable extension constraint was violated
224    #[msg("A pausable extension constraint was violated")]
225    ConstraintMintPausableExtension,
226    /// 2044 - A pausable extension authority constraint was violated
227    #[msg("A pausable extension authority constraint was violated")]
228    ConstraintMintPausableAuthority,
229
230    // Require
231    /// 2500 - A require expression was violated
232    #[msg("A require expression was violated")]
233    RequireViolated = 2500,
234    /// 2501 - A require_eq expression was violated
235    #[msg("A require_eq expression was violated")]
236    RequireEqViolated,
237    /// 2502 - A require_keys_eq expression was violated
238    #[msg("A require_keys_eq expression was violated")]
239    RequireKeysEqViolated,
240    /// 2503 - A require_neq expression was violated
241    #[msg("A require_neq expression was violated")]
242    RequireNeqViolated,
243    /// 2504 - A require_keys_neq expression was violated
244    #[msg("A require_keys_neq expression was violated")]
245    RequireKeysNeqViolated,
246    /// 2505 - A require_gt expression was violated
247    #[msg("A require_gt expression was violated")]
248    RequireGtViolated,
249    /// 2506 - A require_gte expression was violated
250    #[msg("A require_gte expression was violated")]
251    RequireGteViolated,
252
253    // Accounts.
254    /// 3000 - The account discriminator was already set on this account
255    #[msg("The account discriminator was already set on this account")]
256    AccountDiscriminatorAlreadySet = 3000,
257    /// 3001 - No discriminator was found on the account
258    #[msg("No discriminator was found on the account")]
259    AccountDiscriminatorNotFound,
260    /// 3002 - Account discriminator did not match what was expected
261    #[msg("Account discriminator did not match what was expected")]
262    AccountDiscriminatorMismatch,
263    /// 3003 - Failed to deserialize the account
264    #[msg("Failed to deserialize the account")]
265    AccountDidNotDeserialize,
266    /// 3004 - Failed to serialize the account
267    #[msg("Failed to serialize the account")]
268    AccountDidNotSerialize,
269    /// 3005 - Not enough account keys given to the instruction
270    #[msg("Not enough account keys given to the instruction")]
271    AccountNotEnoughKeys,
272    /// 3006 - The given account is not mutable
273    #[msg("The given account is not mutable")]
274    AccountNotMutable,
275    /// 3007 - The given account is owned by a different program than expected
276    #[msg("The given account is owned by a different program than expected")]
277    AccountOwnedByWrongProgram,
278    /// 3008 - Program ID was not as expected
279    #[msg("Program ID was not as expected")]
280    InvalidProgramId,
281    /// 3009 - Program account is not executable
282    #[msg("Program account is not executable")]
283    InvalidProgramExecutable,
284    /// 3010 - The given account did not sign
285    #[msg("The given account did not sign")]
286    AccountNotSigner,
287    /// 3011 - The given account is not owned by the system program
288    #[msg("The given account is not owned by the system program")]
289    AccountNotSystemOwned,
290    /// 3012 - The program expected this account to be already initialized
291    #[msg("The program expected this account to be already initialized")]
292    AccountNotInitialized,
293    /// 3013 - The given account is not a program data account
294    #[msg("The given account is not a program data account")]
295    AccountNotProgramData,
296    /// 3014 - The given account is not the associated token account
297    #[msg("The given account is not the associated token account")]
298    AccountNotAssociatedTokenAccount,
299    /// 3015 - The given public key does not match the required sysvar
300    #[msg("The given public key does not match the required sysvar")]
301    AccountSysvarMismatch,
302    /// 3016 - The account reallocation exceeds the MAX_PERMITTED_DATA_INCREASE limit
303    #[msg("The account reallocation exceeds the MAX_PERMITTED_DATA_INCREASE limit")]
304    AccountReallocExceedsLimit,
305    /// 3017 - The account was duplicated for more than one reallocation
306    #[msg("The account was duplicated for more than one reallocation")]
307    AccountDuplicateReallocs,
308
309    // Miscellaneous
310    /// 4100 - The declared program id does not match actual program id
311    #[msg("The declared program id does not match the actual program id")]
312    DeclaredProgramIdMismatch = 4100,
313    /// 4101 - You cannot/should not initialize the payer account as a program account
314    #[msg("You cannot/should not initialize the payer account as a program account")]
315    TryingToInitPayerAsProgramAccount = 4101,
316    /// 4102 - Invalid numeric conversion error
317    #[msg("Error during numeric conversion")]
318    InvalidNumericConversion = 4102,
319
320    // Deprecated
321    /// 5000 - The API being used is deprecated and should no longer be used
322    #[msg("The API being used is deprecated and should no longer be used")]
323    Deprecated = 5000,
324}
325
326#[derive(Debug, PartialEq, Eq)]
327pub enum Error {
328    AnchorError(Box<AnchorError>),
329    ProgramError(Box<ProgramErrorWithOrigin>),
330}
331
332impl std::error::Error for Error {}
333
334impl Display for Error {
335    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
336        match self {
337            Error::AnchorError(ae) => Display::fmt(&ae, f),
338            Error::ProgramError(pe) => Display::fmt(&pe, f),
339        }
340    }
341}
342
343impl From<AnchorError> for Error {
344    fn from(ae: AnchorError) -> Self {
345        Self::AnchorError(Box::new(ae))
346    }
347}
348
349impl From<ProgramError> for Error {
350    fn from(program_error: ProgramError) -> Self {
351        Self::ProgramError(Box::new(program_error.into()))
352    }
353}
354
355#[cfg(feature = "borsh")]
356impl From<borsh::io::Error> for Error {
357    fn from(error: borsh::io::Error) -> Self {
358        Error::ProgramError(Box::new(ProgramError::from(error).into()))
359    }
360}
361
362impl From<ProgramErrorWithOrigin> for Error {
363    fn from(pe: ProgramErrorWithOrigin) -> Self {
364        Self::ProgramError(Box::new(pe))
365    }
366}
367
368impl From<TryFromIntError> for Error {
369    fn from(e: TryFromIntError) -> Self {
370        Self::AnchorError(Box::new(AnchorError {
371            error_name: ErrorCode::InvalidNumericConversion.name(),
372            error_code_number: ErrorCode::InvalidNumericConversion.into(),
373            error_msg: format!("{e}"),
374            error_origin: None,
375            compared_values: None,
376        }))
377    }
378}
379
380impl From<std::convert::Infallible> for Error {
381    fn from(_: std::convert::Infallible) -> Self {
382        unreachable!("Infallible has no inhabitants")
383    }
384}
385
386impl Error {
387    pub fn log(&self) {
388        match self {
389            Error::ProgramError(program_error) => program_error.log(),
390            Error::AnchorError(anchor_error) => anchor_error.log(),
391        }
392    }
393
394    pub fn with_account_name(mut self, account_name: impl ToString) -> Self {
395        match &mut self {
396            Error::AnchorError(ae) => {
397                ae.error_origin = Some(ErrorOrigin::AccountName(account_name.to_string()));
398            }
399            Error::ProgramError(pe) => {
400                pe.error_origin = Some(ErrorOrigin::AccountName(account_name.to_string()));
401            }
402        };
403        self
404    }
405
406    pub fn with_source(mut self, source: Source) -> Self {
407        match &mut self {
408            Error::AnchorError(ae) => {
409                ae.error_origin = Some(ErrorOrigin::Source(source));
410            }
411            Error::ProgramError(pe) => {
412                pe.error_origin = Some(ErrorOrigin::Source(source));
413            }
414        };
415        self
416    }
417
418    pub fn with_pubkeys(mut self, pubkeys: (Pubkey, Pubkey)) -> Self {
419        let pubkeys = Some(ComparedValues::Pubkeys((pubkeys.0, pubkeys.1)));
420        match &mut self {
421            Error::AnchorError(ae) => ae.compared_values = pubkeys,
422            Error::ProgramError(pe) => pe.compared_values = pubkeys,
423        };
424        self
425    }
426
427    pub fn with_values(mut self, values: (impl ToString, impl ToString)) -> Self {
428        match &mut self {
429            Error::AnchorError(ae) => {
430                ae.compared_values = Some(ComparedValues::Values((
431                    values.0.to_string(),
432                    values.1.to_string(),
433                )))
434            }
435            Error::ProgramError(pe) => {
436                pe.compared_values = Some(ComparedValues::Values((
437                    values.0.to_string(),
438                    values.1.to_string(),
439                )))
440            }
441        };
442        self
443    }
444}
445
446#[derive(Debug)]
447pub struct ProgramErrorWithOrigin {
448    pub program_error: ProgramError,
449    pub error_origin: Option<ErrorOrigin>,
450    pub compared_values: Option<ComparedValues>,
451}
452
453// Two ProgramErrors are equal when they have the same error code
454impl PartialEq for ProgramErrorWithOrigin {
455    fn eq(&self, other: &Self) -> bool {
456        self.program_error == other.program_error
457    }
458}
459impl Eq for ProgramErrorWithOrigin {}
460
461impl Display for ProgramErrorWithOrigin {
462    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
463        Display::fmt(&self.program_error, f)
464    }
465}
466
467impl ProgramErrorWithOrigin {
468    pub fn log(&self) {
469        match &self.error_origin {
470            None => {
471                solana_msg::msg!(
472                    "ProgramError occurred. Error Code: {:?}. Error Number: {}. Error Message: {}.",
473                    self.program_error,
474                    u64::from(self.program_error.clone()),
475                    self.program_error
476                );
477            }
478            Some(ErrorOrigin::Source(source)) => {
479                solana_msg::msg!(
480                    "ProgramError thrown in {}:{}. Error Code: {:?}. Error Number: {}. Error \
481                     Message: {}.",
482                    source.filename,
483                    source.line,
484                    self.program_error,
485                    u64::from(self.program_error.clone()),
486                    self.program_error
487                );
488            }
489            Some(ErrorOrigin::AccountName(account_name)) => {
490                // using sol_log because msg! wrongly interprets 5 inputs as u64
491                solana_msg::msg!(&format!(
492                    "ProgramError caused by account: {}. Error Code: {:?}. Error Number: {}. \
493                     Error Message: {}.",
494                    account_name,
495                    self.program_error,
496                    u64::from(self.program_error.clone()),
497                    self.program_error
498                ));
499            }
500        }
501        match &self.compared_values {
502            Some(ComparedValues::Pubkeys((left, right))) => {
503                solana_msg::msg!("Left:");
504                left.log();
505                solana_msg::msg!("Right:");
506                right.log();
507            }
508            Some(ComparedValues::Values((left, right))) => {
509                solana_msg::msg!("Left: {}", left);
510                solana_msg::msg!("Right: {}", right);
511            }
512            None => (),
513        }
514    }
515
516    pub fn with_source(mut self, source: Source) -> Self {
517        self.error_origin = Some(ErrorOrigin::Source(source));
518        self
519    }
520
521    pub fn with_account_name(mut self, account_name: impl ToString) -> Self {
522        self.error_origin = Some(ErrorOrigin::AccountName(account_name.to_string()));
523        self
524    }
525}
526
527impl From<ProgramError> for ProgramErrorWithOrigin {
528    fn from(program_error: ProgramError) -> Self {
529        Self {
530            program_error,
531            error_origin: None,
532            compared_values: None,
533        }
534    }
535}
536
537#[derive(Debug)]
538pub enum ComparedValues {
539    Values((String, String)),
540    Pubkeys((Pubkey, Pubkey)),
541}
542
543#[derive(Debug)]
544pub enum ErrorOrigin {
545    Source(Source),
546    AccountName(String),
547}
548
549#[derive(Debug)]
550pub struct AnchorError {
551    pub error_name: String,
552    pub error_code_number: u32,
553    pub error_msg: String,
554    pub error_origin: Option<ErrorOrigin>,
555    pub compared_values: Option<ComparedValues>,
556}
557
558impl AnchorError {
559    pub fn log(&self) {
560        match &self.error_origin {
561            None => {
562                solana_msg::msg!(
563                    "AnchorError occurred. Error Code: {}. Error Number: {}. Error Message: {}.",
564                    self.error_name,
565                    self.error_code_number,
566                    self.error_msg
567                );
568            }
569            Some(ErrorOrigin::Source(source)) => {
570                solana_msg::msg!(
571                    "AnchorError thrown in {}:{}. Error Code: {}. Error Number: {}. Error \
572                     Message: {}.",
573                    source.filename,
574                    source.line,
575                    self.error_name,
576                    self.error_code_number,
577                    self.error_msg
578                );
579            }
580            Some(ErrorOrigin::AccountName(account_name)) => {
581                solana_msg::msg!(
582                    "AnchorError caused by account: {}. Error Code: {}. Error Number: {}. Error \
583                     Message: {}.",
584                    account_name,
585                    self.error_name,
586                    self.error_code_number,
587                    self.error_msg
588                );
589            }
590        }
591        match &self.compared_values {
592            Some(ComparedValues::Pubkeys((left, right))) => {
593                solana_msg::msg!("Left:");
594                left.log();
595                solana_msg::msg!("Right:");
596                right.log();
597            }
598            Some(ComparedValues::Values((left, right))) => {
599                solana_msg::msg!("Left: {}", left);
600                solana_msg::msg!("Right: {}", right);
601            }
602            None => (),
603        }
604    }
605
606    pub fn with_source(mut self, source: Source) -> Self {
607        self.error_origin = Some(ErrorOrigin::Source(source));
608        self
609    }
610
611    pub fn with_account_name(mut self, account_name: impl ToString) -> Self {
612        self.error_origin = Some(ErrorOrigin::AccountName(account_name.to_string()));
613        self
614    }
615}
616
617impl Display for AnchorError {
618    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
619        Debug::fmt(&self, f)
620    }
621}
622
623/// Two `AnchorError`s are equal when they have the same error code
624impl PartialEq for AnchorError {
625    fn eq(&self, other: &Self) -> bool {
626        self.error_code_number == other.error_code_number
627    }
628}
629
630impl Eq for AnchorError {}
631
632impl std::convert::From<Error> for ProgramError {
633    fn from(e: Error) -> ProgramError {
634        match e {
635            Error::AnchorError(error) => ProgramError::Custom(error.error_code_number),
636            Error::ProgramError(program_error) => program_error.program_error,
637        }
638    }
639}
640
641#[derive(Debug)]
642pub struct Source {
643    pub filename: &'static str,
644    pub line: u32,
645}