Transactions Not Landing
A transaction that simulates cleanly can still never confirm: leaders skip it during congestion, blockhashes expire, or fee bids lose the priority auction. Production triage separates execution failure from inclusion failure before you change program logic.
Recipe
Quick-reference recipe card - copy-paste ready.
import {
createSolanaRpc,
createSolanaRpcSubscriptions,
pipe,
createTransactionMessage,
setTransactionMessageFeePayer,
setTransactionMessageLifetimeUsingBlockhash,
appendTransactionMessageInstructions,
signTransactionMessageWithSigners,
getSignatureFromTransaction,
} from "@solana/kit";
import {
getSetComputeUnitLimitInstruction,
getSetComputeUnitPriceInstruction,
} from "@solana-program/compute-budget";
// 1. Fresh blockhash 2. Compute budget ix 3. send + subscribeWhen to reach for this:
- Users see "pending" indefinitely in the wallet
- Error rate is low but support volume for stuck txs is high
- Post-deploy spike in unconfirmed signatures
- Competing bots during NFT mint or token launch
- You need SLOs for transaction landing latency
Working Example
import { createSolanaRpc, createSolanaRpcSubscriptions } from "@solana/kit";
const rpc = createSolanaRpc(process.env.RPC_URL!);
const subs = createSolanaRpcSubscriptions(process.env.WS_URL!);
async function sendWithLandingWatch(wireBase64: string, signature: string) {
await rpc.sendTransaction(wireBase64, {
encoding: "base64",
skipPreflight: false,
maxRetries: 0,
preflightCommitment: "confirmed",
}).send();
const started = Date.now();
const sub = await subs.signatureNotifications(signature, { commitment: "confirmed" });
for await (const note of sub) {
if (note.value.err) throw new Error(`On-chain err: ${JSON.stringify(note.value.err)}`);
return { landedMs: Date.now() - started, slot: note.context.slot };
}
throw new Error("Subscription ended without confirmation");
}What this demonstrates:
maxRetries: 0so your app controls retry policy with fresh blockhashes- WebSocket
signatureNotificationsinstead of pollinggetSignatureStatuses - Measuring landing latency for SLO dashboards
- Treating on-chain
errseparately from never landing
Deep Dive
How It Works
- Gulf Stream forwards transactions to upcoming leaders; there is no guaranteed global mempool ordering.
- Recent blockhash sets transaction lifetime (~60-90 seconds of validity). Expired txs are dropped silently.
- Priority fee (
set_compute_unit_price) bids micro-lamports per CU; higher bids tend to land first under load. - Compute unit limit caps execution; under-bidding CU can cause scheduling inefficiency.
- Address Lookup Tables (ALTs) shrink tx size so more txs fit per block.
Landing Failure Modes
| Signal | Likely cause | Mitigation |
|---|---|---|
| No signature in explorer | RPC never forwarded | Secondary provider, check 429 rate limits |
| Signature exists, no confirmation | Dropped / outbid | Raise priority fee, bundle via Jito |
BlockhashNotFound on resend | Stale lifetime | Fetch new blockhash, re-sign |
| Confirmed then rolled back | Rare fork; usually wrong commitment read | Wait for confirmed or finalized |
TypeScript Notes
// Fetch recent prioritization fees for your program accounts
const fees = await rpc.getRecentPrioritizationFees({
lockedWritableAccounts: [programId],
}).send();
const p75 = fees.value.map((f) => f.prioritizationFee).sort((a, b) => a - b)[
Math.floor(fees.value.length * 0.75)
] ?? 1;Gotchas
- Identical resubmits - Rebroadcasting the same signed bytes after expiry wastes fees. Fix: Rebuild message with new blockhash and re-sign.
- Default CU price of zero during mints - Your txs lose every auction. Fix: Dynamic fee oracle from
getRecentPrioritizationFees. - Trusting
processedfor UX - Forks can unwindprocessed. Fix: Show success atconfirmed; settle money atfinalizedif needed. - Huge transactions without ALTs - Exceeds packet size; RPC may accept but leaders drop. Fix: Compile with lookup tables; split instructions.
- RPC retry storms - Client and RPC both retry; nonce confusion and rate limits. Fix: App-level idempotency keys;
maxRetries: 0on send. - Ignoring time-of-day congestion - Mainnet peaks differ from devnet. Fix: Track landing p95 by hour; auto-scale fee multiplier.
Alternatives
| Alternative | Use When | Don't Use When |
|---|---|---|
| Jito bundles | Must land atomically with tip | Simple low-value transfers |
| Private RPC / SWQoS | Production dApp baseline | Local development only |
| Lower commitment UX | Read-only dashboards | Payment confirmation |
| Transaction queue service | Backend batching with fee policy | Fully client-side wallets |
FAQs
Is a dropped transaction a program bug?
Usually no. Inclusion failures are economic and network-scheduling problems unless simulation also fails.
How long is a blockhash valid?
Roughly 60-90 seconds (150 slots). Always fetch a fresh one before resubmit.
Should I use `skipPreflight: true` to land faster?
Only on trusted server paths with separate simulation. Skipping preflight increases on-chain failure rate.
What priority fee should I use?
Start at the p75 of getRecentPrioritizationFees for your writable accounts; tune from landing metrics.
Why does devnet land instantly but mainnet does not?
Devnet has low contention. Mainnet fee markets and leader scheduling dominate.
Do ALTs help landing?
They reduce tx size, improving propagation and fit in blocks - especially for complex DeFi routes.
How do I detect silent drops?
Timeout on signatureNotifications; compare against getSignatureStatuses on a second RPC.
Can I resubmit the same signature?
Yes - duplicate sends of identical wire bytes are idempotent until expiry. After expiry you need a new signature.
What is SWQoS?
Stake-weighted QoS gives staked-connection RPC providers better forwarding to leaders - common in paid tiers.
When should I use Jito bundles?
High-contention mints, arbitrage, or any flow where atomic landing justifies a tip.
Related
- Debugging Failed Transactions - simulation and program errors
- RPC & Infrastructure Outages - provider failover
- Before/After: Making Transactions Land Reliably - case study
Stack versions: This page was written for Agave 4.1.1, Solana CLI 3.0.10, Anchor 0.32.1, anchor-lang 0.32.1, Rust 1.91.1, @solana/kit 7.0.0, Surfpool 0.12.0, and LiteSVM 0.6.x.