How to handle TransactionExpiredBlockheightExceededError when submitting a transaction on Solana

I’m working on a Solana project where I need to swap tokens using the Jupiter API. The code occasionally runs properly (successful transaction), but I usually get an issue where the transaction expired and failed with the TransactionExpiredBlockheightExceededError even after retrying with a new blockhash.

I’ve tried hardcoding prioritizationFeeLamports to a set value instead of auto, and also tried using dynamicComputeUnitLimit: true

import { Connection, Keypair, VersionedTransaction, TransactionExpiredBlockheightExceededError } from '@solana/web3.js';
import fetch from 'cross-fetch';
import { Wallet } from '@project-serum/anchor';
import bs58 from 'bs58';
import { privateKey } from './header.js';
import { publicKey } from './header.js';

// Connection to the Solana mainnet
const connection = new Connection('https://api.mainnet-beta.solana.com', 'confirmed');

// Convert privateKey to a Uint8Array using bs58 decoding
const wallet = new Wallet(Keypair.fromSecretKey(bs58.decode(privateKey)));

// Define the mint addresses as constants
const INPUT_MINT = 'So11111111111111111111111111111111111111112';
const OUTPUT_MINT = 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v';
const amount = 10000000;

// Define other parameters
const slippageBps = 50;

// Use the constants in the URL
const url = `https://quote-api.jup.ag/v6/quote?inputMint=${INPUT_MINT}&outputMint=${OUTPUT_MINT}&amount=${amount}&slippageBps=${slippageBps}`;
console.log(url);  // Just to check the URL with the constants

// Fetch quote response
const quoteResponse = await fetch(url).then(res => res.json());
console.log({ quoteResponse });

// Get serialized transactions for the swap
const { swapTransaction } = await (
  await fetch('https://quote-api.jup.ag/v6/swap', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      quoteResponse,
      userPublicKey: publicKey,
      wrapAndUnwrapSol: true,
      prioritizationFeeLamports: "auto",
    })
  })
).json();

// Deserialize the transaction
const swapTransactionBuf = Buffer.from(swapTransaction, 'base64');
var transaction = VersionedTransaction.deserialize(swapTransactionBuf);
console.log(transaction);

// Sign the transaction
transaction.sign([wallet.payer]);

// Execute the transaction
const rawTransaction = transaction.serialize();
console.log(rawTransaction);

const sendTransaction = async () => {
  let txid;  // Declare txid here to use it later in case of retry
  try {
    // Fetch the latest blockhash just before sending the transaction
    const latestBlockHash = await connection.getLatestBlockhash();
    console.log(latestBlockHash);

    // Send the transaction
    txid = await connection.sendRawTransaction(rawTransaction, {
      skipPreflight: true,
      maxRetries: 2
    });

    // Confirm the transaction
    await connection.confirmTransaction({
      blockhash: latestBlockHash.blockhash,
      lastValidBlockHeight: latestBlockHash.lastValidBlockHeight,
      signature: txid
    });

    console.log(`Transaction confirmed: https://solscan.io/tx/${txid}`);
  } catch (error) {
    if (error instanceof TransactionExpiredBlockheightExceededError) {
      console.error('Transaction expired, resubmitting...');
      
      // Retry by getting the latest blockhash again
      const newBlockHash = await connection.getLatestBlockhash();
      console.log('Using new blockhash for retry:', newBlockHash);
      
      // Update transaction with the new blockhash
      const retryTransaction = VersionedTransaction.deserialize(swapTransactionBuf);
      retryTransaction.sign([wallet.payer]);
      
      // Resend with the new blockhash
      txid = await connection.sendRawTransaction(retryTransaction.serialize(), {
        skipPreflight: true,
        maxRetries: 2
      });

      console.log("Retrying with new blockhash...");

      // Confirm the transaction again with the new blockhash
      await connection.confirmTransaction({
        blockhash: newBlockHash.blockhash,
        lastValidBlockHeight: newBlockHash.lastValidBlockHeight,
        signature: txid
      });

      console.log(`Transaction retried and confirmed: https://solscan.io/tx/${txid}`);
    } else {
      console.error('Error confirming transaction:', error);
    }
  }
};

sendTransaction();

Output:

TransactionExpiredBlockheightExceededError: Signature 2PT8n78c9YC5CH7Kp6YfBHgwMHe67iRpirQN4aNKSqDAo8uYwenxCyhWjYzUKnb3u4GdLBgrw37jLkXgq9bWBQUK has expired: block height exceeded.