Why "submitted" is not "landed"
The most common mistake in Solana development is treating a successful sendTransaction response as
success. It isn't. That response only means an RPC node accepted your bytes and put them on a path. It
says nothing about whether the transaction ever reached the producing leader, made it into a block, or
expired in transit.
When traders say "my transaction failed," they almost always mean it never landed - it disappeared silently. That's a different problem from an on-chain failure like a slippage revert or an insufficient balance. On-chain failures at least produce a signature you can inspect. A dropped transaction leaves nothing. Understanding the difference is the first step to fixing it.
A transaction that "failed" usually never failed on-chain. It vanished somewhere between your client and the leader, and no signature was ever recorded to tell you why.
The real reasons transactions drop
Almost every "my transaction vanished" story traces back to one of four causes. Each has a concrete fix.
| Cause | Fix |
|---|---|
| Expired blockhash | A blockhash is valid ~150 slots (~60–90s). Fetch a fresh one right before signing and submit promptly |
| Priority fee too low | Under load, leaders order by compute-unit price. Set a dynamic, competitive fee from current conditions instead of a flat default |
| Never reached the leader | Forwarding through RPC hops adds delay and drop risk. Deliver directly to the current and upcoming leaders' TPUs |
| Dropped under congestion | When the leader's ingest is saturated, unstaked connections lose packets first. Use stake-weighted QoS for guaranteed bandwidth |
The first three are about correctness and delivery. The fourth is the one most people miss, and it has become the dominant landing factor during the busy windows where landing actually matters.
Stake-weighted QoS (SWQoS) explained
When a Solana leader is producing a block, every client on the network is trying to push transactions into the same QUIC ingress at once. There is finite capacity. Historically that capacity was a shared free-for-all: under congestion, packets were dropped more or less indiscriminately, and a well-funded bot and a hobbyist endpoint competed on equal footing for a lane that couldn't fit either of them.
Stake-weighted QoS (SWQoS) changes the allocation. Instead of one shared lane, the leader reserves ingestion bandwidth in proportion to stake. A connection that originates from - or is sponsored by - a staked validator gets a guaranteed slice of the leader's QUIC capacity, sized to that validator's stake. The more stake behind your connection, the more of the leader's attention you're entitled to when everyone is shouting at once.
The practical effect: during exactly the congested slots where landing is hardest, a stake-weighted connection sails through while unstaked traffic gets throttled or dropped. If you've ever watched your landing rate collapse the moment the network gets busy - the moment landing matters most - SWQoS is very likely the missing piece.
Get to the leader: direct TPU delivery
Bandwidth allocation only matters if your bytes actually arrive at the right door. That door is the TPU - the Transaction Processing Unit - the QUIC ingress on the current leader. A naive path sends your transaction to a generic RPC node, which forwards it onward toward whoever it thinks the leader is. Every forward is added latency and another chance to be dropped under load.
Because Solana's leader schedule is known for the whole epoch in advance, you don't have to guess. You can resolve the current leader and the next few upcoming leaders and deliver straight to their TPUs over QUIC, so whoever builds the next block already has your transaction in hand. rpc edge's transaction sender does exactly this, and pairs it with stake-weighted connections so your delivery isn't just direct, it's prioritized at ingest. We walk through the leader schedule and TPU mechanics in detail in Landing Transactions on Solana: Leader Paths and the Jito Block Engine.
Priority fees: buying your place in line
Once your transaction is in front of the leader, the leader still has to choose what to include. When it has more transactions than fit, it orders them by priority fee - the compute-unit price you attach. A higher fee raises your position in that ordering.
The keyword is dynamic. A flat priority fee is wrong in both directions: too low and you get deprioritized the instant the network gets busy, too high and you're donating lamports during quiet periods. The right approach reads current network conditions and sets a competitive-but-not-wasteful compute-unit price per transaction. And remember the fee's limits - it improves your standing only after delivery and ingest have succeeded. Pay a huge fee on a transaction that never reaches the leader and you've paid for nothing.
Jito bundles for atomicity and bidding
Direct TPU delivery is the right tool for a single transaction you just want to land. Some strategies need more: atomicity and ordering. That's the Jito Block Engine.
A Jito bundle is a set of transactions that execute all-or-nothing, in the exact order you specify. You attach a tip to a Jito tip account, and the Block Engine runs an auction - the highest-tipping bundles win inclusion and ordering. This is the mechanism behind competitive MEV on Solana: you're not just sending, you're bidding for the right to be included exactly how you want. Reach for it when you need atomic multi-transaction execution, guaranteed ordering, or you want to bid for inclusion. For a plain single transaction, direct delivery is simpler and cheaper.
// fresh blockhash, direct to leaders, you own the retries
const tx = await buildTx({ blockhash: latest.blockhash }); // refreshed right before signing
const sig = await rpcedge.sendTransaction(tx, {
route: "tpu", // direct to current + upcoming leaders' TPUs
leaders: 3, // fan out to the next few producers
swqos: true, // stake-weighted ingest bandwidth under congestion
skipPreflight: true, // you already simulated; don't pay for it again
maxRetries: 0, // you control retries; see below
});Retries done right
Blindly retrying is how you pay twice or land a stale action. Set maxRetries: 0 on the RPC and own the
retry loop yourself: resubmit the same signed transaction to fresh leaders within the blockhash window,
stop the moment it lands or the blockhash expires, and never silently re-send something your strategy no
longer wants. A retry is a delivery decision, not a default. This is also why polling for confirmation is
the wrong tool - you want to be pushed the moment your transaction lands, not poll and wait, which we
cover in Why RPC Polling Can't Keep Up With HFT on Solana.
Land transactions like you mean it.
rpc edge's transaction sender delivers to current and upcoming leaders' TPUs with stake-weighted QoS and the Jito Block Engine - co-located, tuned for landing rate.
The takeaway
When a Solana transaction "fails," ask first whether it landed at all - usually it didn't. Fix the four delivery failures in order: refresh the blockhash, fee dynamically, deliver straight to the leaders' TPUs, and lean on stake-weighted QoS so your packets are accepted when the network is saturated. Reach for Jito bundles when you need atomicity, ordering, or to bid. Own your retries. And put the whole path next to the cluster - rpc edge runs its RPC and transaction sender beside Solana stake clusters and the Jito Block Engine, because every part of landing is a delivery race, and delivery races are won by proximity. That's the difference between hoping a transaction lands and engineering it to.