Vesting · Errors · 12@tokenops/sdk/fhe-vesting
Vesting errors you can catch by class.
Catch these by class reference; each carries the offending value (vestingId, unlocksAt, feeType, etc.) so your UI can render a message specific to the failure mode.
For the catch-ladder pattern + how SDK-level and viem-passthrough errors fit alongside these, read Concepts › Typed errors + recovery.
| Class | When thrown | Recovery |
|---|---|---|
| VestingNotFoundError | vestingId does not exist on this manager clone. | User is likely on the wrong manager. Offer the correct address; the SDK exposes useRecipientVestings to discover their vestings on a manager. |
| ClaimLockedError | The schedule’s timelock has not elapsed yet (block timestamp < startTimestamp + timelock). | err.context.unlocksAt is the Unix timestamp the lock lifts. Render a countdown. useClaim |
| VestingExpiredError | Schedule passed its end timestamp and any final claim window closed. | Nothing to claim. Offer to fall through to the next schedule, or surface 'fully claimed'. |
| VestingRevokedError | Operator already revoked this vesting; useClaim/usePartialClaim on a revoked schedule. | Read state via useVestedAmount / useGetClaimableAmount — both honor revocation. Render 'revoked' instead of a claim button. |
| VestingNotRevocableError | useRevokeVesting called on a vesting created with isRevocable=false. | Revocability is set at vesting open and immutable. The UI should hide the revoke action unless useVestingMetadata.isRevocable is true. |
| TransferAlreadyPendingError | useInitiateVestingTransfer called while a pending transfer already exists for this vestingId. | Check usePendingVestingTransfer first; cancel the prior one before opening a new transfer. |
| TransferExpiredError | useAcceptVestingTransfer called after the transfer's expiry timestamp. | err.context.expiredAt shows when the offer lapsed. Initiator must call useInitiateVestingTransfer again to issue a fresh offer. |
| AccessDeniedError | Caller lacks the role the call requires (VESTING_CREATOR_ROLE, REVOKER_ROLE, PAUSER_ROLE, DISCLOSURE_ADMIN_ROLE, …). | err.context.role is the bytes32 role identifier. Check useHasRole + the role-constants table on the manager. |
| BatchTooLargeError | useBatchCreateVesting passed more entries than the manager allows in a single tx. | Chunk the batch. The on-chain cap is encoded in the contract; surface a 'too many entries' UI rather than the raw error. |
| FeatureDisabledError | Operator called a feature the manager wasn't configured for (e.g. split on a non-split manager). | Configuration flags are immutable at clone-time. Hide the action in the UI if useManagerConfig flags it disabled. |
| InsufficientBalanceError | useCreateVesting: encrypted source balance is too low for the requested amount. | Re-read the encrypted balance with useDecryptedHandle; surface the cap to the user before they submit. |
| MissingEncryptorError | A write hook that encrypts client-side ran without an encryptor injected. | Pass encryptor in ManagerHookOptions (parent scope) or via args.encryptor on the mutate call. See /concepts/encryptor. |
TokenOpsSdkError and carries the offending values under err.context — render specific messages instead of generic "transaction failed."Read the catch ladder