The three tiers#
SDK-level errors. Network, RPC, encryptor setup, ABI mismatch. Throw types like MissingEncryptorError, DeploymentAddressUnavailableError, UnsupportedChainError. Thrown before the contract is touched.
Product-level errors. The contract reverted with a known reason and the SDK decoded it into a class. Vesting examples: VestingNotFoundError, ClaimLockedError, ContractRevertError. Each class carries the offending values as fields so your UI can render a specific message.
viem passthrough. Contract reverted with a reason the SDK doesn't have a specific class for. You get a viem ContractFunctionRevertedError with the raw reason string. Catch this as a last resort.
The catch ladder#
import {
VestingNotFoundError,
ClaimLockedError,
ClaimNotStartedError,
} from "@tokenops/sdk/fhe-vesting";
import { ContractFunctionRevertedError } from "viem";
try {
await manager.claim(args);
} catch (err) {
if (err instanceof VestingNotFoundError) {
// Product-level: the vesting id doesn't exist on this manager.
// err.context.vestingId is the offending value.
showInline("That vesting doesn't exist. Did you switch managers?");
} else if (err instanceof ClaimLockedError) {
// Product-level: cliff not reached.
showInline(`Locked until ${new Date(err.context.unlocksAt * 1000)}.`);
} else if (err instanceof ContractFunctionRevertedError) {
// viem-passthrough: contract reverted, no SDK class wrapping yet.
showInline(`Contract reverted: ${err.shortMessage}`);
} else {
// SDK-level: network, RPC, encryptor failure, etc.
showInline("Network error. Try again.");
}
}Recovery patterns by tier#
SDK-level: retry with backoff (network), prompt the user (encryptor / wallet), or fix the deployment surface (chain config).
Product-level: recoverable per error. Examples:
ClaimLockedError→ show countdown toerr.context.unlocksAtContractRevertError→ refresh fee viauseManagerFeeInfo, retry with the right branchVestingNotFoundError→ user is on the wrong manager; offer the correct address
viem passthrough: show shortMessage inline. Don't blame the user — the SDK should have a class for this; treat it as "we'll be better next release."