Transaction simulation failed: Blockhash not found
TransactionExpiredBlockheightExceededError: Signature ... has expired: block height exceeded.
Both errors come from the same design decision. A Solana transaction doesn't carry a nonce. Instead it carries a recent blockhash, and that blockhash is what stops the transaction being replayed. It's also why transactions expire, and why a transaction that was "sent" can quietly never land.
How recent blockhashes work
When you build a transaction you include recentBlockhash. A validator only processes it if that blockhash is among the last 150 blocks it knows about. After that the transaction is permanently invalid and can never be executed. That is roughly a minute or so of wall-clock time, depending on slot times.
getLatestBlockhash returns two things, and you need both:
const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash("confirmed");
lastValidBlockHeight is the last block height at which a transaction using this blockhash can still be included. Once the chain's block height passes it, your transaction is dead. That's the only reliable signal that it's safe to build and sign a new one.
Why "Blockhash not found" happens
This error usually comes from preflight simulation on the RPC node, not from the network itself. Common causes:
- The blockhash is too new for the node that simulated it. You fetched it from one RPC node (or with
processedcommitment) and sent it to another node that hasn't seen that block yet. Load-balanced RPC endpoints make this easy to hit. - The blockhash came from a fork that was dropped. A
processedblockhash can belong to a block that never gets confirmed. Transactions referencing it can never land. - The blockhash is too old. You fetched it once, then signed later: after a wallet prompt, a hardware wallet confirmation, or a slow queue.
- Commitment mismatch. You fetched the blockhash at but the preflight ran at , where that block isn't visible yet.