slymnoyann/hey-1 · error · TransactionWaitError

Transaction confirmation timed out

Error message

Transaction confirmation timed out

What it means

Thrown as TransactionWaitError by useWaitForTransactionToComplete when a exponential-backoff polling loop (delay doubling up to MAX_DELAY) exhausts its attempts without the transaction reaching a confirmed status via getTransactionStatus. It's a timeout, not a rejection: the tx may still confirm later.

Source

Thrown at src/hooks/useWaitForTransactionToComplete.tsx:44

        const { data } = await getTransactionStatus({
          variables: { request: { txHash: hash } }
        });

        const status = data?.transactionStatus;

        if (status?.__typename === "FinishedTransactionStatus") {
          return;
        }

        if (status?.__typename === "FailedTransactionStatus") {
          throw new TransactionWaitError(status.reason);
        }

        await new Promise((resolve) => setTimeout(resolve, delay));
        delay = Math.min(delay * 2, MAX_DELAY);
      }

      throw new TransactionWaitError("Transaction confirmation timed out");
    },
    [getTransactionStatus]
  );

  return waitForTransactionToComplete;
};

export default useWaitForTransactionToComplete;

View on GitHub (pinned to 88c8f9d553)

Solutions

  1. Retry with the same hash: waiting is idempotent and the tx often confirms on a second pass
  2. Switch to a more reliable/faster RPC endpoint or official chain RPC for getTransactionStatus
  3. Verify the tx on a block explorer by hash before retrying — if it reverted, surface a revert error instead of looping
  4. Increase MAX_DELAY/attempt budget for congested periods, or use wagmi's waitForTransactionReceipt which subscribes instead of polling

Example fix

// before
throw new TransactionWaitError("Transaction confirmation timed out");

// after: include the hash and make it retryable
class TransactionWaitError extends Error {
  constructor(public txHash: string) {
    super(`Transaction ${txHash} confirmation timed out`);
  }
}
throw new TransactionWaitError(hash);
Defensive patterns

Strategy: retry

Validate before calling

// Check status on an explorer/RPC before waiting, to catch already-final or reverted txs
const receipt = await publicClient.getTransactionReceipt({ hash });
if (receipt) {
  if (receipt.status === "reverted") throw new Error("Transaction reverted");
  return; // already confirmed
}

Type guard

const isTransactionWaitError = (e: unknown): e is Error & { name: "TransactionWaitError" } =>
  e instanceof Error && e.message === "Transaction confirmation timed out";

Try / catch

try {
  await waitForTransactionToComplete(hash);
} catch (e) {
  if (isTransactionWaitError(e)) {
    // timeout != failure: check explorer by hash; retry the wait once
    await sleep(5000);
    return waitForTransactionToComplete(hash);
  }
  throw e;
}

Prevention

When it happens

Trigger: A slow or congested chain where the tx takes longer than the loop's total budget; the tx hash not yet visible to the status RPC/indexer (node lag, indexer behind head); RPC endpoint flakiness returning pending/unknown; or the tx actually reverted and never reaches 'success' status.

Common situations: Gas spikes or NFT mint congestion on Polygon; using a free RPC with slow finality propagation; a reorg invalidating the observed hash; API indexer (Lens Hub indexer) lagging behind the chain so status stays pending.

Understand the failure class


AI-assisted analysis of slymnoyann/hey-1@88c8f9d553 (2026-08-28). Data as JSON: /api/errors/f937b187b6141b92. Report an issue: GitHub.