errors.mdx 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321
  1. ---
  2. title: Custom Errors
  3. description: Learn how to implement custom error handling in Anchor programs.
  4. ---
  5. All instruction handlers in Anchor programs return a custom `Result<T>` type
  6. that allows you to handle successful execution with `Ok(T)` and error cases with
  7. `Err(Error)`.
  8. ```rust
  9. // [!code word:Result]
  10. pub fn custom_instruction(ctx: Context<CustomInstruction>) -> Result<()> {
  11. // --snip--
  12. Ok(())
  13. }
  14. ```
  15. The
  16. [`Result<T>`](https://github.com/coral-xyz/anchor/blob/v0.30.1/lang/src/lib.rs#L74)
  17. type in Anchor programs is a type alias that wraps the standard Rust
  18. `Result<T, E>`. In this case, `T` represents the successful return type, while
  19. `E` is Anchor's custom `Error` type.
  20. ```rust
  21. pub type Result<T> = std::result::Result<T, error::Error>;
  22. ```
  23. ## Anchor Error
  24. When an error occurs in an Anchor program, it returns a custom
  25. [`Error`](https://github.com/coral-xyz/anchor/blob/v0.30.1/lang/src/error.rs#L277-L281)
  26. type defined as:
  27. ```rust
  28. #[derive(Debug, PartialEq, Eq)]
  29. pub enum Error {
  30. AnchorError(Box<AnchorError>),
  31. ProgramError(Box<ProgramErrorWithOrigin>),
  32. }
  33. ```
  34. The `Error` type in Anchor programs can be one of two variants:
  35. 1. [`ProgramErrorWithOrigin`](https://github.com/coral-xyz/anchor/blob/v0.30.1/lang/src/error.rs#L389-L394):
  36. Custom type that wraps a standard Solana
  37. [`ProgramError`](https://github.com/anza-xyz/agave/blob/v1.18.26/sdk/program/src/program_error.rs#L12-L66)
  38. type. These errors come from the `solana_program` crate.
  39. ```rust
  40. #[derive(Debug)]
  41. pub struct ProgramErrorWithOrigin {
  42. pub program_error: ProgramError,
  43. pub error_origin: Option<ErrorOrigin>,
  44. pub compared_values: Option<ComparedValues>,
  45. }
  46. ```
  47. 2. [`AnchorError`](https://github.com/coral-xyz/anchor/blob/v0.30.1/lang/src/error.rs#L490-L497):
  48. Errors defined by the Anchor framework.
  49. ```rust
  50. #[derive(Debug)]
  51. pub struct AnchorError {
  52. pub error_name: String,
  53. pub error_code_number: u32,
  54. pub error_msg: String,
  55. pub error_origin: Option<ErrorOrigin>,
  56. pub compared_values: Option<ComparedValues>,
  57. }
  58. ```
  59. An `AnchorError` can be thought of as having two categories:
  60. 1. Internal Anchor Errors - These are built-in errors included with the Anchor
  61. framework. They are defined in the
  62. [`ErrorCode`](https://github.com/coral-xyz/anchor/blob/v0.30.1/lang/src/error.rs#L10-L275)
  63. enum.
  64. 2. Custom Program Errors - These are program specific errors that developers
  65. define to handle custom error cases.
  66. The `error_code_number` from an `AnchorError` has the following numbering
  67. scheme:
  68. | Error Code | Description |
  69. | ---------- | ------------------------------------- |
  70. | >= 100 | Instruction error codes |
  71. | >= 1000 | IDL error codes |
  72. | >= 2000 | Constraint error codes |
  73. | >= 3000 | Account error codes |
  74. | >= 4100 | Misc error codes |
  75. | = 5000 | Deprecated error code |
  76. | >= 6000 | Starting point for custom user errors |
  77. ## Usage
  78. Anchor provides a convenient way to define custom errors through the
  79. `error_code` attribute. The implementation details can be found
  80. [here](https://github.com/coral-xyz/anchor/blob/master/lang/syn/src/codegen/error.rs).
  81. When you define an enum with the `error_code` attribute, Anchor automatically:
  82. - Assigns an error code starting from 6000
  83. - Generates the necessary boilerplate for error handling
  84. - Enables the use of custom error messages via the `msg` attribute
  85. ```rust
  86. #[error_code]
  87. pub enum MyError {
  88. #[msg("My custom error message")]
  89. MyCustomError,
  90. #[msg("My second custom error message")]
  91. MySecondCustomError,
  92. }
  93. ```
  94. ### err!
  95. To throw an error, use the
  96. [`err!`](https://github.com/coral-xyz/anchor/blob/v0.30.1/lang/src/lib.rs#L720-L728)
  97. macro. The `err!` macro provides a convenient way to return custom errors from
  98. your program. Under the hood, `err!` uses the `error!` macro to construct
  99. `AnchorError`. The implementation can be found
  100. [here](https://github.com/coral-xyz/anchor/blob/v0.30.1/lang/attribute/error/src/lib.rs#L84-L116).
  101. ```rust
  102. #[program]
  103. mod hello_anchor {
  104. use super::*;
  105. pub fn set_data(ctx: Context<SetData, data: MyAccount) - Result<()> {
  106. if data.data = 100 {
  107. // [!code word:MyError]
  108. // [!code highlight]
  109. return err!(MyError::DataTooLarge);
  110. }
  111. ctx.accounts.my_account.set_inner(data);
  112. Ok(())
  113. }
  114. }
  115. #[error_code]
  116. pub enum MyError {
  117. #[msg("MyAccount may only hold data below 100")]
  118. DataTooLarge
  119. }
  120. ```
  121. ### require!
  122. The
  123. [`require!`](https://github.com/coral-xyz/anchor/blob/v0.30.1/lang/src/lib.rs#L510-L522)
  124. macro provides a more concise way to handle error conditions. It combines a
  125. condition check with returning an error if the condition is false. Here's how we
  126. can rewrite the previous example using `require!`:
  127. ```rust
  128. #[program]
  129. mod hello_anchor {
  130. use super::*;
  131. pub fn set_data(ctx: Context<SetData, data: MyAccount) - Result<()> {
  132. // [!code word:MyError]
  133. // [!code highlight]
  134. require!(data.data < 100, MyError::DataTooLarge);
  135. ctx.accounts.my_account.set_inner(data);
  136. Ok(())
  137. }
  138. }
  139. #[error_code]
  140. pub enum MyError {
  141. #[msg("MyAccount may only hold data below 100")]
  142. DataTooLarge
  143. }
  144. ```
  145. Anchor provides several "require" macros for different validation needs. You can
  146. find the implementation of these macros
  147. [here](https://github.com/coral-xyz/anchor/blob/v0.30.1/lang/src/lib.rs).
  148. | Macro | Description |
  149. | ------------------- | ------------------------------------------------------------------------------------------- |
  150. | `require!` | Ensures a condition is true, otherwise returns with the given error. |
  151. | `require_eq!` | Ensures two NON-PUBKEY values are equal. |
  152. | `require_neq!` | Ensures two NON-PUBKEY values are not equal. |
  153. | `require_keys_eq!` | Ensures two pubkeys values are equal. |
  154. | `require_keys_neq!` | Ensures two pubkeys are not equal. |
  155. | `require_gt!` | Ensures the first NON-PUBKEY value is greater than the second NON-PUBKEY value. |
  156. | `require_gte!` | Ensures the first NON-PUBKEY value is greater than or equal to the second NON-PUBKEY value. |
  157. ## Example
  158. Here's a simple example demonstrating how to define and handle custom errors in
  159. an Anchor program. The program below validates that an input amount falls within
  160. an acceptable range, showing how to:
  161. - Define custom error types with messages
  162. - Use the `require!` macro to check conditions and return errors
  163. <Tabs items={["Program", "Client"]}>
  164. <Tab value="Program">
  165. ```rust title="lib.rs"
  166. use anchor_lang::prelude::*;
  167. declare_id!("9oECKMeeyf1fWNPKzyrB2x1AbLjHDFjs139kEyFwBpoV");
  168. #[program]
  169. pub mod custom_error {
  170. use super::*;
  171. pub fn validate_amount(_ctx: Context<ValidateAmount, amount: u64) - Result<()> {
  172. // [!code word:CustomError]
  173. // [!code highlight:2]
  174. require!(amount = 10, CustomError::AmountTooSmall);
  175. require!(amount <= 100, CustomError::AmountTooLarge);
  176. msg!("Amount validated successfully: {}", amount);
  177. Ok(())
  178. }
  179. }
  180. #[derive(Accounts)]
  181. pub struct ValidateAmount {}
  182. #[error_code]
  183. pub enum CustomError {
  184. #[msg("Amount must be greater than or equal to 10")]
  185. AmountTooSmall,
  186. #[msg("Amount must be less than or equal to 100")]
  187. AmountTooLarge,
  188. }
  189. ```
  190. </Tab>
  191. <Tab value="Client">
  192. ```ts title="test.ts"
  193. import * as anchor from "@coral-xyz/anchor";
  194. import { Program } from "@coral-xyz/anchor";
  195. import { CustomError } from "../target/types/custom_error";
  196. import assert from "assert";
  197. describe("custom-error", () = {
  198. anchor.setProvider(anchor.AnchorProvider.env());
  199. const program = anchor.workspace.CustomError as Program<CustomError;
  200. it("Successfully validates amount within range", async () = {
  201. const tx = await program.methods.validateAmount(new anchor.BN(50)).rpc();
  202. console.log("Transaction signature:", tx);
  203. });
  204. it("Fails with amount too small", async () = {
  205. try {
  206. await program.methods.validateAmount(new anchor.BN(5)).rpc();
  207. assert.fail("Expected an error to be thrown");
  208. } catch (error) {
  209. assert.strictEqual(error.error.errorCode.code, "AmountTooSmall");
  210. assert.strictEqual(
  211. error.error.errorMessage,
  212. "Amount must be greater than or equal to 10",
  213. );
  214. }
  215. });
  216. it("Fails with amount too large", async () = {
  217. try {
  218. await program.methods.validateAmount(new anchor.BN(150)).rpc();
  219. assert.fail("Expected an error to be thrown");
  220. } catch (error) {
  221. assert.strictEqual(error.error.errorCode.code, "AmountTooLarge");
  222. assert.strictEqual(
  223. error.error.errorMessage,
  224. "Amount must be less than or equal to 100",
  225. );
  226. }
  227. });
  228. });
  229. ```
  230. </Tab>
  231. </Tabs>
  232. When a program error occurs, Anchor's TypeScript Client SDK returns a detailed
  233. [error response](https://github.com/coral-xyz/anchor/blob/v0.30.1/ts/packages/anchor/src/error.ts#L51-L71)
  234. containing information about the error. Here's an example error response showing
  235. the structure and available fields:
  236. ```shell title="Error Response"
  237. {
  238. errorLogs: [
  239. 'Program log: AnchorError thrown in programs/custom-error/src/lib.rs:11. Error Code: AmountTooLarge. Error Number: 6001. Error Message: Amount must be less than or equal to 100.'
  240. ],
  241. logs: [
  242. 'Program 9oECKMeeyf1fWNPKzyrB2x1AbLjHDFjs139kEyFwBpoV invoke [1]',
  243. 'Program log: Instruction: ValidateAmount',
  244. 'Program log: AnchorError thrown in programs/custom-error/src/lib.rs:11. Error Code: AmountTooLarge. Error Number: 6001. Error Message: Amount must be less than or equal to 100.',
  245. 'Program 9oECKMeeyf1fWNPKzyrB2x1AbLjHDFjs139kEyFwBpoV consumed 2153 of 200000 compute units',
  246. 'Program 9oECKMeeyf1fWNPKzyrB2x1AbLjHDFjs139kEyFwBpoV failed: custom program error: 0x1771'
  247. ],
  248. error: {
  249. errorCode: { code: 'AmountTooLarge', number: 6001 },
  250. errorMessage: 'Amount must be less than or equal to 100',
  251. comparedValues: undefined,
  252. origin: { file: 'programs/custom-error/src/lib.rs', line: 11 }
  253. },
  254. _programErrorStack: ProgramErrorStack {
  255. stack: [
  256. [PublicKey [PublicKey(9oECKMeeyf1fWNPKzyrB2x1AbLjHDFjs139kEyFwBpoV)]]
  257. ]
  258. }
  259. }
  260. ```
  261. For a more comprehensive example, you can also reference the
  262. [errors test program](https://github.com/coral-xyz/anchor/blob/master/tests/errors/programs/errors/src/lib.rs)
  263. in the Anchor repository.