Vesting 2.0 errors you can catch by class.
Catch by class or branch on error.code. Each error carries the offending values in context. The same classes are re-exported from /fhe-vesting/react, including TokenOpsValidationError, except TokenOpsContractError, which a component catching useEnsureOperator receipt failures imports from @tokenops/sdk/fhe-vesting or /fhe/react.
For the catch-ladder pattern + how SDK-level and generic-fallback errors fit alongside these, read Concepts › Typed errors + recovery.
| Class | When thrown | Recovery |
|---|---|---|
| VestingNotFoundError | vestingId does not exist on this manager clone. | The user is likely on the wrong manager. useRecipientVestings lists a recipient's schedules on a manager. |
| NotVestingRecipientError | claim / partialClaim from an account that is not the schedule's recipient. | On the revert path context is { method, contractAddress, vestingId }; preflightClaim also fills caller and expectedRecipient. Show 'this vesting belongs to another account' instead of a claim button. useClaim |
| ClaimLockedError | The schedule's timelock has not elapsed. | context.unlocksAt is the Unix time the lock lifts, when known. Render a countdown. useClaim |
| VestingExpiredError | revokeVesting / batchRevokeVesting on a schedule past its endTimestamp. Claiming an ended schedule does not revert. | Hide the revoke action after endTimestamp; context.endedAt when known. |
| VestingRevokedError | The schedule is already revoked: a second revoke, or a split of a revoked source. | Render 'revoked' from useVestingInfo (revokeTimestamp) instead of the action. |
| VestingNotRevocableError | revokeVesting on a schedule created with isRevocable: false. | Revocability is fixed at creation. Gate the action on useVestingInfo(...).data?.isRevocable. |
| TransferAlreadyPendingError | initiateVestingTransfer while a transfer is pending, including an expired one still in storage. | Read usePendingVestingTransfer; the current recipient cancels with useCancelVestingTransfer before a new offer. |
| NotPendingRecipientError | acceptVestingTransfer from an address that is not the pending recipient, or with no pending transfer. | Only the named recipient can accept. Show the address from usePendingVestingTransfer. useAcceptVestingTransfer |
| TransferExpiredError | acceptVestingTransfer after the offer expired. | The recipient cancels the stale offer, then the initiator issues a fresh one. |
| NoPendingTransferError | cancelVestingTransfer with no transfer pending. | Gate the cancel action on usePendingVestingTransfer returning an offer. |
| OperatorNotApprovedError | createVesting / batchCreateVesting before the manager clone is an ERC-7984 operator on the token. Also a preflightCreateVesting blocker. | Call setOperator({ token, spender: manager }) or useEnsureOperator. When known, the message names the holder, the spender and the token. useEnsureOperator |
| AccessDeniedError | The caller lacks the role the call needs (VESTING_CREATOR_ROLE, REVOKER_ROLE, PAUSER_ROLE, ...). | context.role is the bytes32 role when known. Check useHasRole({ role, holder }) and grant it. useHasRole |
| InsufficientBalanceError | preflightClaim: the claimant's ETH balance is below the gas fee on a FeeType.Gas manager. It was InsufficientFeeError in 1.6. | context is { balanceKind: "eth", requested: fee, available: balance }. Ask the user to top up; branch on TOKENOPS_INSUFFICIENT_BALANCE. |
| InsufficientFeeError | A claim whose value is not exactly the manager's gas fee (more or less). On a DistributionToken clone a non-zero value is an InvalidArgumentError. | Pass value from useManagerFeeInfo; the feeType discriminant on ClaimArgs and PartialClaimArgs keeps it from being omitted. useManagerFeeInfo |
| InvalidArgumentError | Input the SDK refuses before sending: batchCreateVesting over MAX_EUINT64_PER_INPUT_PROOF items, batchRevokeVesting with an empty list or duplicate ids, both amount and encryptedInput, an out-of-range uint64. | Fix the input. Range errors no longer copy the amount into context.value, and split numerator errors omit the numerator. |
| BatchTooLargeError | A batch above the manager's on-chain maxBatchSize / maxRevokeBatchSize. | context.requested (number) and context.max (bigint) are 0 on the vesting revert path, since the contract error carries no arguments. Chunk the batch; read useManagerMaxBatchSize / useManagerMaxRevokeBatchSize for the limit. useManagerMaxBatchSize |
| FeatureDisabledError | splitVesting or pause on a clone deployed with that feature off. | The flags are immutable. Hide the action unless useManagerIsSplitEnabled / useManagerIsPausable is true. useManagerIsSplitEnabled |
| MissingEncryptorError | A write that encrypts ran with no encryptor resolvable. The error names the method you called. | Pass encryptor on the client or hook options (encryptor: () => zamaSDK), or per call where the args accept it. |
| ReceiptEventNotFoundError | createManager's mined receipt has no ManagerCreated event (ReceiptEventAmbiguousError for more than one). Says "The transaction reverted." when it did. | Check the transaction status; for a receipt-free create, read the log yourself once it executes. |
| DeploymentAddressUnavailableError | No factory for the chain: context.reason is registry-not-deployed on mainnet (a null entry) and registry-unknown-chain for a chain the registry does not list. | Switch to Sepolia, or pass address explicitly. |
| PausedError | claim, partialClaim, splitVesting or a transfer call on a paused clone (EnforcedPause). Also a preflightClaim blocker. | Disable the action while useManagerPaused is true; adminClaim is not pause-gated. useManagerPaused |
| FheHandleNotAllowedError | discloseHandleToParty on a handle the caller or the manager may not use (HandleNotAllowed / SenderNotAllowedToUseHandle). | Disclose only handles the caller is allowed on, such as ones returned by the access hooks. |
| TransferFailedError | withdrawGasFee, or withdrawTokenFee on a FeeType.Gas clone, where the transfer reverts. On the token-fee path it carries asset: "eth". | Call withdrawTokenFee only on DistributionToken clones and withdrawGasFee only on Gas clones. |
| WalletChainMismatchError | The wallet's chain differs from the public client's. Refused before estimating gas. | Prompt the wallet to switch chains, then retry. |
TokenOpsSdkError and carries the offending values under err.context — render specific messages instead of generic "transaction failed."Read the catch ladder The shared palette
Wallet, network, relayer and encryption errors (WalletRejectedError, NetworkError, RelayerUnreachableError, EncryptionFailedError, AclNotPropagatedError and the rest) are shared across every product. A client constructor error such as a malformed address reaches a hook as resolutionError rather than a render-time throw.