Why solana transactions sometimes land but instructions silently fail
A transaction that lands on Solana is not a guarantee that anything you asked for actually happened. The network can accept and finalize a transaction while every instruction inside it fails, leaving your balances and positions exactly as they were - minus the fee you paid. This happens because Solana separates the question of whether a transaction is valid from whether its instructions succeed.
The transaction shell vs. the instructions inside
Think of a Solana transaction as an envelope. The envelope has a signature, a recent blockhash, and a list of accounts it touches. The validators check the envelope carefully: Is the signature valid? Is the blockhash recent? Are the accounts properly listed and signed for? If any of those checks fail, the transaction is rejected outright and never enters a block. That part is strict.
But the envelope's contents - the actual instruction data, the function calls, the token transfers - are a separate matter. Once the envelope passes the signature and fee checks, the network considers the transaction "landed." It gets included in a block. Then the runtime executes each instruction one by one. If an instruction hits a problem - an arithmetic overflow, a missing token account, a violated program rule - that instruction fails. And here's the key: the transaction still lands. The block still contains it. The fee is still collected. The failed instruction just doesn't do what you wanted.
Why validators don't reject failed instructions upfront
You might wonder why validators don't just simulate the whole thing first and reject it if any instruction will fail. They could. In fact, the leader does simulate transactions before proposing a block. But the simulation has a different purpose than you might think. It's about resource accounting - figuring out how much compute each transaction will use, how much priority fee it will pay, and whether the block has room. It is not a guarantee that every instruction will succeed.
There are several reasons an instruction can fail after simulation passes. The most common: the simulation runs against a snapshot of state at that moment, but the actual execution happens after other transactions in the same block have already run and modified the same accounts. A program might check "does this account exist?" during simulation, but by the time your transaction executes, another transaction in the same block has already closed that account. The check passes in simulation, fails in reality.
Also, Solana transactions are atomic in a limited sense: if any instruction in a transaction fails, then the entire transaction's state changes are rolled back. So if you have a multi-instruction transaction where the first instruction transfers tokens and the second one does something that fails, the transfer is undone. But the transaction itself is still on-chain, still marked as executed, and you still paid the fee. You can look it up on a block explorer and see the failure reason. The money's gone, the trade didn't happen, and the record is permanent.
The "Success" Flag Is a Lie (Sort of)
Here's where it gets confusing. When you query a Solana RPC for a transaction status, you get two different fields: status and meta.err. The status field tells you whether the transaction was included in a block. It will say Success even if every instruction failed. The meta.err field is the one that tells you what actually happened. If meta.err is null, everything worked. If it's Some(error), an instruction failed.
So when a wallet or a DEX interface says "transaction successful," it often means nothing more than "the transaction landed." The interface didn't decode the meta.err. It just saw the transaction get finalized and assumed the best. This is the single most common source of "it said it worked but nothing happened" complaints on Solana.
How to tell what actually happened
If you're building on Solana, or just trying to figure out why your swap didn't go through, do this:
- Get the transaction signature (the base58 string). Every wallet shows it after submitting.
- Open a block explorer (Solscan, Solana Explorer, XRAY). Paste the signature.
- Look at the "Transaction Details" or "Raw" tab. Find the field labeled
meta.error "Error." If it'snull, every instruction succeeded. If it's an object like{"InstructionError":[0,"Custom(1)"]}, that tells you which instruction failed (index 0) and what the error was (Custom code 1, which you need to look up in the program's source). - Check the "Log Messages" section. This is the most direct route. When a program fails, it usually logs a reason. Solana's runtime includes a log like
Program log: Error: ...before the failure. That message often tells you exactly what went wrong.
Why this design exists
It sounds broken, but there's a method. Solana's performance depends on validators not having to revert state after the fact. By allowing failed instructions to land, the network avoids the complexity of "optimistic execution" and reorgs. It also means that a transaction with a valid signature and a recent blockhash is always includable, even if its logic is nonsense. The fee is charged for the compute used, not for the success of the outcome.
This is also why you should never assume a transaction did something just because it appeared in a block. The block explorer will show you the truth, but you have to look past the green checkmark.
The Practical Takeaway
When a transaction lands but instructions fail, the most likely culprits are:
- Token accounts not created. You tried to receive an SPL token but never made a token account for it. The transfer instruction fails because the destination account doesn't exist. The fix is to create the account first, or use a program that does it atomically.
- Slippage exceeded. A DEX checked the price, and by the time your transaction executed, the price had moved outside your allowed range. The swap instruction fails. The transaction lands. No swap happens.
- Account closed or already used. A program tried to close an account that was already closed, or a nonce was already used. This is common with repeated airdrop claims or when two transactions share the same blockhash and nonce.
- Compute budget exceeded. Your transaction used more compute units than you set as a limit, or than the default allows. The instruction that exceeded the budget fails, and everything after it is rolled back too.
None of these are bugs. They're the system working as designed. The network doesn't know what you meant to do. It only knows what you asked it to do, and if the ask fails, it tells you - in a field most people never read.
The lesson for users: check meta.err, not status. For developers: always decode the error logs, and design your programs to fail loudly and clearly, with custom error codes and descriptive log messages, so the next person who hits this can figure out why.
Not financial advice. orymsolana.xyz publishes market data and general information about digital assets. Crypto assets are volatile and you can lose everything you put in. Nothing here is a recommendation to buy, sell or hold, and we make no price predictions.
Prices are sourced from third parties and may be delayed or wrong. Verify anything you intend to act on against a primary source.