Retrying and Confirming Transactions Correctly
Sending a Solana transaction is fire-and-forget: sendTransaction returns a signature, but does not guarantee inclusion. You must poll for confirmation and retry intelligently when transactions fail to land.
The confirmation loop
import { createSolanaRpc } from "@solana/kit";
const rpc = createSolanaRpc("https://api.mainnet-beta.solana.com");
async function sendAndConfirm(signedTx: string) {
const { value: { blockhash, lastValidBlockHeight } } =
await rpc.getLatestBlockhash({ commitment: "confirmed" }).send();
const signature = await rpc.sendTransaction(signedTx, {
encoding: "base64",
skipPreflight: true,
}).send();
// Poll until confirmed or blockhash expires
while (true) {
const [status, height] = await Promise.all([
rpc.getSignatureStatuses([signature]).send(),
rpc.getBlockHeight({ commitment: "confirmed" }).send(),
]);
if (status.value[0]?.confirmationStatus === "confirmed") {
return signature; // success
}
if (height > lastValidBlockHeight) {
return null; // expired — caller should rebuild and retry
}
await new Promise((r) => setTimeout(r, 2000));
}
}
Key principles
1. Track blockhash expiry, not time
Do not use a fixed timeout. Instead, compare the current block height against lastValidBlockHeight from getLatestBlockhash. This is the network’s own expiry signal.
2. Resend the same transaction while the blockhash is valid
While the blockhash has not expired, resending the same signed transaction is safe. It has the same signature, so Solana processes it at most once. Resending every 2-3 seconds increases the chance a leader receives it.
3. Rebuild after blockhash expires
Once the block height exceeds lastValidBlockHeight, the transaction can never land. Fetch a new blockhash, rebuild the transaction, re-sign it, and send again.
4. Check before retrying
Always check getSignatureStatuses before concluding a transaction failed. It may have confirmed while you were waiting.
Common mistakes
| Mistake | Problem | Fix |
|---|---|---|
| Using SDK timeout as the only check | Transaction may confirm after timeout | Poll getSignatureStatuses independently |
| Retrying with a new blockhash without checking | May send duplicate transactions | Check if original signature confirmed first |
| Not resending while blockhash is valid | Only one chance to reach a leader | Resend every 2-3 seconds |
Using processed commitment | Can report false positives | Use confirmed for reliable confirmation |
FAQ
Can a transaction execute twice if I resend it? No. Each transaction has a unique signature. The network deduplicates by signature. The same signed transaction can only execute once.
What commitment level should I check?
Use confirmed for most applications. finalized provides the strongest guarantee (31+ confirmations) but takes ~12 seconds longer.
How long should I wait between polls? 2-3 seconds is typical. Polling more frequently wastes RPC requests without benefit — slot time is ~400ms, but confirmation requires multiple validators.
Should I use durable nonces? Only for special cases: offline signing, multisig approvals, or transactions that must survive long delays. Durable nonces add complexity (an extra instruction, a nonce account) and are not needed for standard real-time transactions.
Updated: