Common Errors

Revert selectors and the role table for the errors most likely to block a Fusion vault deployment.

Revert selectors and setup errors an agent or developer is likely to hit while deploying or operating a Fusion vault (see Deploy your first Fusion vault), with the cause and the fix for each. The SDK README's Common errors and the MCP resource fusion://invariants carry the same deployment checks.

Revert table

SelectorRevertWhat happenedFix
0x8745fbfdDaoFeePackagesArrayEmpty()clone() was sent to IporFusionFactoryImplSend it to IporFusionFactoryProxy for that chain — see Addresses
0x9996b315AddressEmptyCode(address)execute() touched a market with no balance fuseadd_balance_fuse(market_id, balance_fuse) before the first execute on that market
0x4bf4de4eHandlerNotFound()A protocol called back into the vault during execute() (a flash loan) and no handler is registered for its address and selectorupdate_callback_handler(handler, sender, selector) before the first execute that triggers the callback
0x068ca9d8AccessManagedUnauthorized(address)The caller lacks the role the function requires: FUSE_MANAGER for add_fuses, grant_market_substrates, add_balance_fuse, update_callback_handler; ALPHA for execute; WHITELIST for deposit and mint on a private vaultCheck first with AccessManager.can_call(caller, target, selector).call(ctx) (a closed target or a missing role answers immediate=False, delay=0; a role with an execution delay answers its delay). AccessManager.grant_role(role, account, Period(0)) from the role's admin (OWNER grants ATOMIST, ATOMIST grants the rest); for a reverting deposit, whitelist the depositor or convert the vault to public
ValueError: Private key required for sending transactionsSDK, before any transaction.send(ctx) on a Web3Context without a keyWeb3Context(w3, chain_id, signer=..., private_key=...) or Web3Context.from_url(url, private_key=...)

A positive can_call(...).call(ctx).delay means the call must be scheduled, not that another role grant is missing: AccessManager.read_call_plan(ctx, call, caller=...) tells a missing schedule from a waiting or a ready one. For withdrawals, redemptions and share transfers also read get_account_lock_time(account).call(ctx): a deposit or mint locks its caller, which can differ from the share receiver. Neither check replaces a simulation.

For an execute batch, fusion vault preflight <vault> --calldata <hex> --sender <account> --chain-id base (SDK: preflight_execute, MCP: vault_preflight) checks the permission, the registered fuses, the balance dependencies and a simulated execution together. Market limits take Wad fractions of total assets: Wad(6 * 10**17) is 60%, and a token amount such as 1_000_000 sets a near-zero ceiling; a limit outside 0..10**18 is rejected before encoding.

Role table

RoleIDGrantsGranted by
OWNER_ROLE1Everything below, via the access managerSet at vault creation from the owner argument to clone()
ATOMIST_ROLE100Vault configuration authority; administers FUSE_MANAGER, ALPHA and WHITELISTOWNER
ALPHA_ROLE200Can call executeATOMIST
FUSE_MANAGER_ROLE300Can call add_fuses, grant_market_substrates, add_balance_fuse, update_callback_handlerATOMIST
WHITELIST_ROLE800Can deposit / mint into a private vaultATOMIST

A fresh clone grants the owner only OWNER_ROLE — every configuration and execute call from that account reverts AccessManagedUnauthorized until ATOMIST_ROLE is granted first (it administers the rest), matching step 2 of Deploy your first Fusion vault.

  • Clone from IporFusionFactoryProxy, never from IporFusionFactoryImpl — only the proxy carries the fee configuration.
  • Register all three of add_fuses, grant_market_substrates and add_balance_fuse before the first execute on a market. Any order between them works; what is not optional is that all three precede execute.
  • A fresh clone is private: deposit and mint revert until either the depositor holds WHITELIST_ROLE (keeps the vault private) or an Atomist calls convert_to_public_vault() (opens deposits to every address, and cannot be reverted).
  • A flash-loan fuse also needs update_callback_handler(handler, sender, selector) before its first execute: Morpho Blue calls back onMorphoFlashLoan(uint256,bytes), the Euler EVC onEulerFlashLoan(bytes), and an unregistered callback reverts HandlerNotFound().
  • Fuses are immutable and addresses are per chain — resolve (chain, name) in ipor-abi (opens in new tab); never reuse an address across chains.

Last updated