Understanding Errors on Solana

2024-09-05

The goal of this article is to help you understand how to read and interpret Solana errors. We'll look at several examples of common error messages, break down their structure, and explain how to find the meaning behind these cryptic codes. By the end, you should have a better understanding of how to troubleshoot issues in your Solana transactions and understand what's happening behind the scenes.

When I started my Solana journey in 2021, the biggest difficulty I had was interpreting errors. When I sent a transaction I often would get cryptic answers back.

Like, what does a { InstructionError: (0, { Custom: 1 } ) } error even mean? 🤔 I used to get that all the time.

Even now, it's hard to find the documentation on errors (this is the closest I could find)

In all cases, the error will depend on the context of the transaction and the programs the instructions in that transactions use.

A transaction on Solana is comprised of many instructions. Every instruction interacts with a program to perform an action.

Example 1: Memo Program

Let's see how to add a memo to a transaction in JavaScript:

import { TransactionInstruction, PublicKey } from "@solana/web3.js";
 
const memoInstruction = new TransactionInstruction({
	keys: [{ pubkey, isSigner: true, isWritable: true }],
	data: Buffer.from(memo, "utf-8"),
	programId: new PublicKey("MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"),
});

There are many elements on this transaction creation function, but we're going to focus on the programId. It calls the memo program id on the solana network. In this case, it is MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr. If we search on chain, we find this data

Not very helpful, is it? What errors does this program id throw? How can we identify it.

We need to dig a little deeper.

Luckily, most used programs in Solana are open source. So let's find the Rust implementation (or an IDL) of that program to get a better understanding of what it actually does.

In the lib.rs file, we can see that all this program does, is append up to 544 bytes of data to the transaction. so probably, if you send more data that that it will throw an error.

But where are those errors exactly? In the processor.rs file.

Here, we can find only one error. The InvalidInstructionData. It's not very descriptive, but it tells us something went wrong.

So, this instruction has only one possible error. If this throws an error, it' index is 0.

What does the index have to do with anything? Well, if we would build a transaction with only this instruction, add more than 544 bytes of data, sign it and broadcast it to the network, we would get this error: { InstructionError: (0, { Custom: 0 } ) } See now how this makes sense?

We would get an error on the first instruction, 0, and on the first error code, also 0.

Example 2: Token Program

Let's look at another example. This one is I think the more common one for any starting Solana developer: { InstructionError: (0, { Custom: 1 } ) }. It would come up to me all the time and I used to be very confused.

In this case, it would come up to me when sending (or simulating) a Solana transaction which first instruction sends native SOL tokens or SPL tokens, like USDC.

In both cases, it means the same thing, even though they use different program ids!

How? They have the same error footprints:

{
  0: "NotRentExempt: Lamport balance below rent-exempt threshold",
  1: "InsufficientFunds: Insufficient funds for the operation requested",
  2: "InvalidMint: Invalid Mint",
  3: "MintMismatch: Account not associated with this Mint",
  4: "OwnerMismatch: Owner does not match",
  5: "FixedSupply: This token's supply is fixed and new tokens cannot be minted",
  6: "AlreadyInUse: The account cannot be initialized because it is already being used",
  7: "InvalidNumberOfProvidedSigners: Invalid number of provided signers",
  8: "InvalidNumberOfRequiredSigners: Invalid number of required signers",
  9: "UninitializedState: State is uninitialized",
  10: "NativeNotSupported: Instruction does not support native tokens",
  11: "NonNativeHasBalance: Non-native account can only be closed if its balance is zero",
  12: "InvalidInstruction: Invalid instruction",
  13: "InvalidState: State is invalid for requested operation",
  14: "Overflow: Operation overflowed",
  15: "AuthorityTypeNotSupported: Account does not support specified authority type",
  16: "MintCannotFreeze: This token mint cannot freeze accounts",
  17: "AccountFrozen: Account is frozen; all account operations will fail",
  18: "MintDecimalsMismatch: The provided decimals value different from the Mint decimals",
  19: "NonNativeNotSupported: Instruction does not support non-native tokens",
}

You can find those errors in the official Solana github repo.

What did that cryptic error mean then? I didn't have enough balance to send!

Example 3: Jupiter Swap Program

As a final example, let's look at when a program's code is not publicly available, like the Jupiter swap program.

Oftentimes for this program, I get another cryptic error: { InstructionError: (0, { Custom: 6001 } ) }.

A quick google search yields the IDL for this program. Altough the program's code is not public, we still can get information about it, like which methods it has, what arguments they accept and their errors. In this case, the error footprint is:

{
  6000: "EmptyRoute: Empty route",
  6001: "SlippageToleranceExceeded: Slippage tolerance exceeded",
  6002: "InvalidCalculation: Invalid calculation",
  6003: "MissingPlatformFeeAccount: Missing platform fee account",
  6004: "InvalidSlippage: Invalid slippage",
  6005: "NotEnoughPercent: Not enough percent to 100",
  6006: "InvalidInputIndex: Token input index is invalid",
  6007: "InvalidOutputIndex: Token output index is invalid",
  6008: "NotEnoughAccountKeys: Not Enough Account keys",
  6009: "NonZeroMinimumOutAmountNotSupported: Non zero minimum out amount not supported",
  6010: "InvalidRoutePlan: Invalid route plan",
  6011: "InvalidReferralAuthority: Invalid referral authority",
  6012: "LedgerTokenAccountDoesNotMatch: Token account doesn't match the ledger",
  6013: "InvalidTokenLedger: Invalid token ledger",
  6014: "IncorrectTokenProgramID: Token program ID is invalid",
  6015: "TokenProgramNotProvided: Token program not provided",
  6016: "SwapNotSupported: Swap not supported",
  6017: "ExactOutAmountNotMatched: Exact out amount doesn't match",
  6018: "SourceAndDestinationMintCannotBeTheSame: Source mint and destination mint cannot the same",
}

That means that my slippage gets exceeded when trading! Happens too often in low liquidity meme coins - my bad for trading them in the first place!

As you can see from this example, error codes can be whatever number the program creator wants, as long as it doesn't repeat.

Hope this helps you understand errors a bit better! Cheers