Solana Error Codes: What Custom 6001 Is Telling You

InstructionError, custom program errors, and why 0x1771 means different things in different programs. How to decode a failure — and spot one that never landed.

BoltTx Team··10 min read
solanaerror-codesdebugginginstruction-errortransaction-landingrpc

A Solana transaction fails and you get something like {"InstructionError":[2,{"Custom":6001}]}. It tells you exactly what happened, once you know how to read it.

Most of the confusion comes from one thing: ★custom error codes are defined by each program, not by Solana.★ The same number means different things depending on which program returned it.

First: Did It Fail, or Did It Never Land?

These look identical in most logs and have completely different fixes.

const { value } = await connection.getSignatureStatuses([sig], {
  searchTransactionHistory: true,
});

if (!value[0]) {
  // ★NEVER LANDED — there is no error to decode.★
  // Fee, routing, retry, or blockhash expiry.
} else if (value[0].err) {
  // Landed and failed. Now the error is real and decodable.
  console.log(JSON.stringify(value[0].err));
}

★A transaction that never landed has no error code, because it never executed.★ Searching for an error that does not exist is how people lose afternoons.

The Shape of an Error

Most failures come back as an InstructionError:

{"InstructionError": [2, {"Custom": 6001}]}
                      ↑        ↑
                      │        └── the program's own error code
                      └── which instruction failed (0-indexed)

★The index matters as much as the code.★ If you prepended two compute budget instructions, index 2 is your first real instruction — not the third thing you wrote.

type TxError = {
  InstructionError?: [number, string | { Custom: number }];
};

const err = value[0].err as TxError;
if (err.InstructionError) {
  const [index, detail] = err.InstructionError;
  const code = typeof detail === "object" ? detail.Custom : detail;
  console.log(`instruction ${index} failed with ${code}`);
}

Runtime Errors: Same Meaning Everywhere

These come from the Solana runtime, so they mean the same thing regardless of program:

Error Meaning Usual cause
InsufficientFundsForRent ★account would drop below rent exemption★ leaving too little SOL
ComputeBudgetExceeded ran out of compute units ★CU limit set too low★
AccountNotFound account does not exist ★missing ATA★
AccountInUse account write-locked this slot contention, often self-inflicted
AlreadyProcessed this signature already landed ★not an error — you succeeded★
BlockhashNotFound blockhash expired or invalid fetched too early
MissingRequiredSignature a required signer did not sign account marked signer incorrectly
ProgramFailedToComplete program panicked usually a bug in the program

Two worth expanding.

AlreadyProcessed is not a failure. It means the signature is already on chain — you retried and an earlier attempt landed. ★Treat it as success, not as an error to retry.★

AccountInUse at high volume is frequently your own doing. Two of your transactions writing the same account in one slot serialise against each other.

If you are decoding errors on transactions that landed but the real problem is transactions not landing at all, a free BoltTx key is one line to test the routing side.

Custom Errors: Program-Specific

Custom: N is defined by whatever program returned it. There is no universal table.

★Anchor programs start user-defined errors at 6000.★ So Custom: 6001 is the second error in that program's error enum:

#[error_code]
pub enum MyError {
    #[msg("Slippage tolerance exceeded")]
    SlippageExceeded,        // 6000
    #[msg("Pool is paused")]
    PoolPaused,              // 6001
}

Non-Anchor programs use whatever numbering they chose. Native programs like the Token Program have their own ranges entirely.

How to decode one:

// The logs almost always contain the human-readable message.
const tx = await connection.getTransaction(sig, {
  maxSupportedTransactionVersion: 0,
});
console.log(tx?.meta?.logMessages?.slice(-10).join("\n"));
// → "Program log: AnchorError ... Error Code: SlippageExceeded. Error Number: 6000"

★Read the logs before searching the hex value.★ Anchor emits the error name directly, which saves you from guessing which program's table applies.

The 0x1771 Confusion

This code appears constantly in Solana search results, usually with a confident answer that it means slippage.

0x1771 is hex for 6001. In an Anchor program, that is ★the second user-defined error★ — whatever the author put there. It happens to be a slippage error in some popular swap programs, which is why the association spread.

0x1770 = 6000   first Anchor user error
0x1771 = 6001   second
0x1772 = 6002   third

★If you are hitting 0x1771 in a program you did not write, look up that program's error enum rather than trusting the folklore.★ Two swap programs can both return 6001 and mean entirely different things.

Token Program Errors

Frequent enough to be worth their own table, since they surface constantly in swap failures:

Code Name Cause
1 InsufficientFunds token balance too low
3 InvalidMint ★mint does not match the account★
4 MintMismatch wrong mint for this token account
5 OwnerMismatch signer does not own the account

InvalidMint and MintMismatch are usually an ATA derivation bug★ — you computed the associated token account for the wrong mint or the wrong owner.

Decoding in Practice

A small function that covers most real cases:

function describeError(err: unknown, logs?: string[]): string {
  const e = err as { InstructionError?: [number, unknown] };
  if (!e?.InstructionError) return JSON.stringify(err);

  const [index, detail] = e.InstructionError;

  if (typeof detail === "string") {
    // Runtime error — same meaning everywhere.
    return `instruction ${index}: ${detail}`;
  }

  const code = (detail as { Custom: number }).Custom;

  // Anchor emits the error name in logs; prefer it over the number.
  const named = logs?.find((l) => l.includes("Error Code:"));
  if (named) return `instruction ${index}: ${named.trim()}`;

  const hint = code >= 6000 ? " (Anchor user-defined)" : "";
  return `instruction ${index}: Custom ${code}${hint}`;
}

Which Errors Are Worth Retrying

★Most are not.★ Retrying a deterministic failure reproduces it and pays the base fee again.

Error Retry? Why
BlockhashNotFound ★rebuild★ old bytes are permanently dead
AccountInUse yes transient contention
AlreadyProcessed ★no — you won★ already on chain
ComputeBudgetExceeded ★no★ raise the limit first
InsufficientFundsForRent no fund the account
Custom slippage error ★maybe★ only with a wider tolerance
MissingRequiredSignature no fix the account metas

★The two most expensive mistakes are retrying ComputeBudgetExceeded without raising the limit, and treating AlreadyProcessed as a failure.★ The first burns fees on a guaranteed failure; the second can make you send a second real transaction when the first already succeeded.

What Landing Looks Like

Real transactions through our delivery nodes: median confirmation 336ms — under one slot.

★Every error code in this article requires the transaction to have landed first.★ If most of your failures return no status at all, error decoding is not where your problem is.

Where BoltTx Fits

We handle getting the transaction into a block. Once it lands, any error is between you and the program.

Submissions route through our own delivery nodes in four regions with stake-weighted routing and no public mempool exposure, so a transaction is not observable in transit before it lands. You sign locally — we never hold funds, never sign, and never modify transaction contents.

The tip travels inside the transaction, paid on chain from your own wallet, and reverts with the transaction if it fails, because that is how Solana handles atomic transactions. You pay only on transactions that reach the chain.

Get a free API key. No monthly fee:

const connection = new Connection("https://la.bolttx.io/?api-key=YOUR_KEY");

FAQ

What does InstructionError mean on Solana? A specific instruction in your transaction failed. The first element is the zero-based index of that instruction, the second is either a runtime error name or a Custom code defined by the program.

What does custom program error 0x1771 mean? 0x1771 is 6001 in decimal, the second user-defined error in an Anchor program. What it means depends entirely on that program's error enum. It is a slippage error in some swap programs, but the number itself carries no universal meaning.

Why do Anchor error codes start at 6000? Anchor reserves lower ranges for its own framework errors and begins user-defined errors at 6000. So a Custom code of 6000 or above in an Anchor program maps to the author's error enum, in declaration order.

What is InsufficientFundsForRent? An account would be left below the balance required for rent exemption. Usually you tried to move out too much SOL, or you are creating an account without funding it to the exemption threshold.

What does AlreadyProcessed mean? The signature is already on chain. It is not a failure — an earlier retry landed. Treat it as success, because retrying can result in sending a second, different transaction when the first already worked.

How do I decode a Solana error code? Read the transaction logs first. Anchor programs emit the error name and number directly, which removes the guesswork. Only fall back to looking up the raw number in a program's error enum if logs are unavailable.

What does AccountNotFound mean in a swap? Usually a missing associated token account. The instruction expected a token account that has never been created, which happens the first time an address holds a given mint.

Why does the instruction index not match my code? Because compute budget instructions count. If you prepended setComputeUnitLimit and setComputeUnitPrice, index 2 is your first real instruction rather than the third one you wrote.

Should I retry a transaction that failed with an error? Usually not. Most errors are deterministic and retrying reproduces them while paying the base fee again. The exceptions are AccountInUse, which is transient, and slippage errors if you widen the tolerance.

What is AccountInUse? Two transactions tried to write the same account in the same slot, so they serialised. At high volume this is often self-inflicted — your own transactions competing for the same writable account.

How do I tell a program error from a runtime error? Runtime errors come back as strings like InsufficientFundsForRent and mean the same thing everywhere. Program errors come back as {"Custom": N} and are defined by that specific program.

What does ComputeBudgetExceeded mean? Your transaction ran out of compute units. The limit was too low for the actual work, often because the default was left in place or a simulation was run against simpler account state than production.

Why did my transaction fail with MissingRequiredSignature? An account was marked as a signer in the instruction but did not sign the transaction. Usually an account meta problem — an account marked isSigner: true that should not be, or a keypair missing from the signing set.

Where do I find a program's error codes? In its IDL if it is an Anchor program, or in its source if published. The error enum is in declaration order starting at 6000 for Anchor user errors. There is no central registry across programs.

My transaction has no error but never appeared on chain. What now? There is no error to decode, because it never executed. Look at blockhash age, priority fee relative to conditions, whether you retried until expiry, and how your transaction was routed.

What are the Token Program error codes? 1 is InsufficientFunds, 3 is InvalidMint, 4 is MintMismatch, 5 is OwnerMismatch. The mint-related ones usually indicate an ATA derived for the wrong mint or the wrong owner.

Back to all posts