Skip to content
Mainnet Live. All executions, addresses and hashes are live.
WORMHOOK
skeletonv0.1 · skeleton docs

Errors

Every error code, where it happens, and whether retrying helps.

Codes are grouped by where in the lifecycle they surface: 1xx source (Solana), 2xx transport and timing, 3xx destination verification, 4xx hook execution, 5xx return path.

CodeNameStageRetryMeaning
WH-101CALLER_NOT_APPROVED2noThe source app is not registered with the router, or it has been paused. Register the app in the developer console, or ask the app owner to unpause it.
WH-102INSUFFICIENT_FEE2yesThe source transaction did not include enough to cover message and delivery fees. Re-quote the route and resend with the quoted fee.
WH-201CALL_EXPIRED6new-callThe HookCall's expiry passed before the destination gateway could execute it. The gateway refused to run a stale call. Prepare a new call with a fresh nonce and a longer TTL. Expired calls are never executed late.
WH-202RELAY_DELAYED5waitThe message is signed, but the executor hasn't delivered it within the route's expected window. No action needed yet. If the call expires first, it will close as WH-201. Manual redelivery is planned.
WH-203RELAY_FAILED5yesThe executor attempted delivery and failed. The signed message is still valid. Request redelivery. The call ID is unchanged, so a retry can't run the hook twice.
WH-301VAA_INVALID6noThe gateway rejected the message: signatures, emitter or chain didn't match the registered router. Nothing to retry. This indicates a misconfigured route or a forged message.
WH-302REPLAY_REJECTED6noThis call ID has already been consumed by the gateway. The second delivery was rejected so the hook could not run twice. Nothing to do. See the original execution for its receipt.
WH-303HOOK_NOT_FOUND6noThe hook ID or version is not in the registry on the destination chain. Check the hook ID and version in the registry.
WH-304HOOK_PAUSED6laterThe hook is registered but currently paused by its owner. Retry with a new call after the hook is unpaused.
WH-305PERMISSION_DENIED6noThis app is not on the hook's allowlist for the requested action. Ask the hook owner to allowlist your app ID.
WH-306SPEND_CAP_EXCEEDED6new-callmaxSpend is above the hook's per-call cap or the app's remaining daily cap. Lower maxSpend, or wait for the daily cap to reset.
WH-401HOOK_REVERTED7dependsThe gateway delivered the call and the hook itself reverted. The gateway caught the revert and emitted a failed receipt, so the Solana side still closes. Read the revert reason in the receipt. Fix the input and send a new call; retrying the same input will revert again.
WH-402ADAPTER_ERROR7dependsThe adapter could not decode the payload or forward it to the hook. Check the payload against the action's input schema.
WH-403PARTIAL_EXECUTION7dependsSome sub-operations succeeded and some didn't. The receipt lists which. Inspect the output. Only retry the failed parts, with a new call.
WH-404OUT_OF_GAS7yesExecution exceeded the gas limit set for delivery. Re-quote with a higher gas limit.
WH-501RECEIPT_PENDING10waitThe hook ran and a receipt was emitted, but it hasn't been recorded on Solana yet. Wait. Return-path latency depends on the destination chain's finality setting.
WH-502RECEIPT_REJECTED10yesThe Solana router rejected the receipt message. Redeliver the receipt. The hook will not run again.

Retry advice

AdviceMeaning
yesSafe to retry. The call ID is unchanged, so the hook still can't run twice.
noRetrying won't change the result.
waitNot terminal. The system is still working on it.
new-callSend a new HookCall (new nonce), usually with adjusted parameters.
laterRetry with a new call once the condition clears (e.g. hook unpaused).
dependsRead the revert data or output; fix the input before sending a new call.

In the SDK

import { WormHookError } from "@wormhook/sdk";

try {
  const receipt = await execution.waitForReceipt();
  if (receipt.status === "failed") {
    // the hook reverted or the call expired: a receipt, not an exception
  }
} catch (e) {
  if (e instanceof WormHookError) {
    e.code;          // "WH-302"
    e.info.retry;    // "no"
    e.info.remedy;   // human-readable next step
  }
}

waitForReceipt() throws only when no receipt can exist for this delivery (WH-302), or when a stalled relay passes the client's timeout (WH-202).

Demo executions