whisk
API reference

Errors

Typed errors thrown by the engine and surfaced through useWhisk — including the machine-readable category field that classifies upstream failures from App Kit.

Every recoverable failure in Whisk is a WhiskError (or a subclass). They carry a stable name and code so you can branch on them without parsing strings, plus an optional machine-readable category that mirrors App Kit's failure classification.

For the broader story — when failures happen, what the widget does automatically, and how to compose the recovery primitives — see Error handling & recovery.

The base class

class WhiskError extends Error {
  readonly code: WhiskErrorCode;
  readonly retryable: boolean;
  readonly step?: StepName; // present for bridge step failures
  readonly category?: WhiskErrorCategory; // machine-readable cause
  readonly cause?: unknown; // underlying throw
}
  • code — high-level category. Stable string literal.
  • retryable — UI hint: "should I show a Retry button?". Authoritative.
  • step — for BridgeStepError, names which step failed (approve / burn / fetchAttestation / mint).
  • category — App Kit's machine-readable classification (added in App Kit 1.4.2+). Use this in switch statements instead of regex on message.
  • cause — the underlying RPC throw / wagmi rejection / etc. Always preserved.

Subclasses

ClasscodeWhen
NoAdapterError"NO_ADAPTER"The source chain has no matching wallet adapter mounted.
WrongChainError"WRONG_CHAIN"The wallet is on a different chain than the quote needs.
InsufficientBalanceError"INSUFFICIENT_BALANCE"Balance < amountIn + fees.
InvalidAddressError"INVALID_ADDRESS"Recipient is malformed or fails checksum.
ResolverError"RESOLVER_FAILED"The resolver chain returned no match.
BridgeStepError"BRIDGE_STEP_FAILED"A specific bridge step failed. Carries the step name.
UserRejectedError"USER_REJECTED"The user declined the wallet prompt.
NetworkError"NETWORK_ERROR"RPC timeout, fetch failure, polling timeout.
WalletCapabilityError"WALLET_CAPABILITY"Wallet doesn't support an EIP-5792 capability (atomic batch, etc.).
OnchainRevertError"ONCHAIN_REVERT"Transaction mined and reverted on chain.
ConfigError"CONFIG_ERROR"The config passed to createWhiskConfig is invalid.

Categories

App Kit 1.4.2+ classifies bridge step failures with a machine-readable errorCategory. Whisk mirrors this on WhiskError.category so apps can switch on cause without string-matching messages.

type WhiskErrorCategory =
  | "user_rejected"
  | "atomic_unsupported" // EIP-5792 wallet_sendCalls unsupported
  | "batch_too_large" // wallet enforces a per-batch cap
  | "duplicate_batch_id" // batch submitted twice
  | "unknown_bundle" // wallet doesn't recognise the batch id
  | "polling_timeout" // wallet_getCallsStatus didn't terminate
  | "failed_offchain" // failed before submission
  | "reverted_onchain" // mined and reverted on chain
  | "partial_reverted" // some calls in a batch reverted
  | "chain_revert" // chain-level revert (reorg / dropped)
  | "unknown"; // uncategorised

The polling_timeout category is what you'd see for the Polygon Amoy → Sei batched-approve issue we documented in testnet quirks — wallet returned a batch id, wallet_getCallsStatus never reached a terminal state inside App Kit's window.

Branching by class

import {
  useWhisk,
  WhiskError,
  UserRejectedError,
  InsufficientBalanceError,
  WalletCapabilityError,
  OnchainRevertError,
} from "@usewhisk/react";

const { state } = useWhisk();

if (state.kind === "failed") {
  const err = state.error;

  if (err instanceof UserRejectedError) {
    // User already knows they cancelled. Silent.
    return null;
  }

  if (err instanceof InsufficientBalanceError) {
    return <p>Top up your USDC and try again.</p>;
  }

  if (err instanceof WalletCapabilityError) {
    // Wallet rejected an EIP-5792 / atomic-batch flow.
    return (
      <p>
        Your wallet doesn't support batched signing on this chain. Try
        connecting a wallet that does, or use the manual recovery flow.
      </p>
    );
  }

  if (err instanceof OnchainRevertError) {
    return <p>Transaction reverted on chain. {err.message}</p>;
  }

  if (err instanceof WhiskError) {
    return <Banner tone="destructive">{err.message}</Banner>;
  }

  // Shouldn't happen. The engine wraps unknowns into a WhiskError.
  Sentry.captureException(err);
  return <p>Something went wrong.</p>;
}

Branching by category (preferred for App Kit failures)

category is set by App Kit's machine-readable classifier and is the authoritative source for why something failed. Prefer it over regex matching on message:

if (state.kind === "failed" && state.error.category) {
  switch (state.error.category) {
    case "user_rejected":
      return null;
    case "polling_timeout":
      return (
        <p>Submission is taking longer than expected; check the explorer.</p>
      );
    case "atomic_unsupported":
    case "batch_too_large":
      return (
        <p>
          Your wallet can't sign this batch atomically. Try a different wallet.
        </p>
      );
    case "reverted_onchain":
    case "partial_reverted":
    case "chain_revert":
      return <p>On-chain failure: {state.error.message}</p>;
    default:
      return <p>{state.error.message}</p>;
  }
}

Normalising unknown throws

If you're calling engine methods directly (not through hooks), toWhiskError(...) converts anything thrown into a WhiskError, preserving the original on error.cause. Pass an optional category when you have an App Kit BridgeStep.errorCategory available — the helper will pick the right subclass:

import { toWhiskError } from "@usewhisk/core";

try {
  await engine.send({ quote, adapter });
} catch (raw) {
  throw toWhiskError(raw); // always a WhiskError after this
}

Reading the underlying cause

The original RPC throw, wagmi rejection, or whatever else is on error.cause. Useful when you want to log the raw error to Sentry but show the typed one to the user:

if (err instanceof NetworkError && err.cause) {
  console.error("Underlying RPC failure:", err.cause);
}

On this page