BlockhashNotFound on sendTransaction
Your transaction failed with `BlockhashNotFound`. You used a blockhash the validator that received your submit no longer has in its recent cache — either stale, or you got it from a different node in a load-balanced pool.
Root causes
Ranked by frequency. First cause is the one to check first.
- 01You fetched the blockhash minutes before signing. Solana invalidates blockhashes after ~2 minutes (150 slots × 400ms).
- 02Your RPC endpoint is a pool, and `getLatestBlockhash` hit one node while `sendTransaction` went to another that's slightly behind in slot height.
- 03You used `commitment: 'processed'` when fetching the blockhash. Some validators reject blockhashes that are processed-only because they haven't fully confirmed yet.
Fix steps
- 1
Always fetch a fresh blockhash immediately before signing
Structure: `const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash('confirmed')`, sign, submit. No async work between those three steps.
- 2
Use commitment: confirmed or finalized
For `getLatestBlockhash`, use `confirmed` at minimum. `processed` blockhashes are too fresh and some validators reject them.
- 3
Route fetch and submit to the same endpoint
Subglow's `sendTransaction` path has its own quota bucket — use the same `https://rpc.subglow.io` for both fetch and submit to guarantee same-backend routing. On credit-metered providers with multiple backend nodes, this consistency is not guaranteed.
- 4
Include the lastValidBlockHeight
Pass `lastValidBlockHeight` to `sendTransaction` / `sendAndConfirmTransaction`. The client will stop retrying once the blockhash expires instead of hammering the endpoint pointlessly.
Code example
import { Connection, Transaction } from "@solana/web3.js";
const connection = new Connection("https://rpc.subglow.io/?api-key=YOUR_KEY", "confirmed");
async function sendSafe(tx: Transaction, signers: Keypair[]) {
const { blockhash, lastValidBlockHeight } =
await connection.getLatestBlockhash("confirmed");
tx.recentBlockhash = blockhash;
tx.lastValidBlockHeight = lastValidBlockHeight;
tx.feePayer = signers[0].publicKey;
tx.sign(...signers);
const sig = await connection.sendRawTransaction(tx.serialize(), {
skipPreflight: true,
maxRetries: 0,
});
await connection.confirmTransaction({ signature: sig, blockhash, lastValidBlockHeight }, "confirmed");
return sig;
}Related errors
- TransactionExpiredBlockheightExceededErrorYour transaction was submitted but not included before its blockhash expired. Either the network is congested and the validator dropped your tx for lack of priority fee, or your submit path is slow enough that 150 slots elapsed between fetch and inclusion.
- Node is behind (slot lag on RPC)Your RPC endpoint is lagging behind the tip of the chain. Symptoms: `getSlot` returns a number much lower than Solana Explorer, `getAccountInfo` returns stale data, `getLatestBlockhash` gives expired blockhashes. Usually a shared-endpoint issue.
- InsufficientFundsForRentYour transaction created an account or ATA but didn't fund it above the rent-exempt minimum. Solana requires every account to pre-pay rent for two years — currently 890,880 lamports (~$0.03) for the smallest useful account.
Want an endpoint that just works?
Subglow is flat-priced Solana gRPC + JSON-RPC on a single API key. Pre-parsed JSON, dedicated sendTransaction bucket, 99.9% latency SLA on Dedicated. No credit juggling, no surprise bills.