Skip to main content

Errors

All custom errors that VaultRegistrar may revert with, plus the relevant inherited errors from OpenZeppelin. Source: bc-vault-registrar/contracts/Errors.sol and the OZ base contracts.

Quick reference table

SelectorErrorWhere it's thrownRecovery
0x0819bdcdSignatureExpired()registerVaultRe-sign with a fresh deadline
0xac94b822InvalidInvestorSignature()registerVaultConfirm operator, token, nonce, chainId, verifyingContract all match
0xfed39497InvestorNotFound(address wallet)registerVaultDrip from faucet (auto-registers) or have admin add to registry
0x38bfcc16VaultAlreadyRegistered(address vault)registerVaultIdempotent — treat as already-done
0x8df63830VaultBelongsToDifferentInvestor(address vault, string vaultInvestorId)registerVaultUse a different vault address
0xe6c4247bInvalidAddress()registerVault, addOperator, removeOperator, initializePass non-zero addresses
0xd6234725NotImplemented()unregisterVaultThe function is intentionally not available; redesign off-ramp
0x308e16ddNotAnOperator(address account)declared on invalidateOperatorPermission; not enforced todayHave admin grant OPERATOR_ROLE

Selector verification. The selectors above are the keccak-256 first-4-byte hashes of the canonical error signatures (e.g. keccak256("SignatureExpired()")[:4] == 0x0819bdcd). Always verify against the deployed bytecode if you're catching them in client code; the canonical signatures are in bc-vault-registrar/contracts/Errors.sol.


SignatureExpired()

error SignatureExpired();

Thrown by registerVault when block.timestamp > deadline. The contract checks the deadline before doing any signature recovery, so this is the cheapest possible failure mode.

Recovery. The investor must produce a new signature with a deadline in the future. Do not retry the same signature — it will keep failing.


InvalidInvestorSignature()

error InvalidInvestorSignature();

Thrown by registerVault when SignatureChecker.isValidSignatureNow(investor, digest, signature) returns false.

Common causes (in rough order of frequency)

  1. The investor signed against the wrong operator field. Submitting through the gateway? The signature must encode the gateway's operator wallet, not your factory.
  2. The investor signed against the wrong token field. Always read registrar.token() first; never hard-code.
  3. The investor signed against a stale nonce. Always read operatorNonce(investor, operator) immediately before signing.
  4. The investor signed against the wrong chainId or verifyingContract in the EIP-712 domain. Both are part of the digest.
  5. The investor signed with a different wallet than the one passed as investorWalletAddress.
  6. The signature was produced over the legacy personal_sign envelope instead of signTypedData_v4.

Recovery. Reproduce the digest off-chain and compare to what your wallet signed. See EIP-712 worked example.


InvestorNotFound(address wallet)

error InvestorNotFound(address wallet);

Thrown by registerVault when registry.getInvestor(investorWalletAddress) returns an empty string. The DS Registry has no entry for this wallet.

Recovery. Two options:

  1. Drip DST to the wallet via the faucet — Faucet.requestTokens() auto-registers new wallets in the registry on the way.
  2. If you control the registry admin, call registry.addWallet(wallet, investorId) directly.

VaultAlreadyRegistered(address vault)

error VaultAlreadyRegistered(address vault);

Thrown by registerVault when the vault address is already bound to the same investor in the registry.

Recovery. Treat as success. The post-condition you wanted (isRegistered(vault, investor) == true) already holds. The contract is conservative about idempotency: it does not silently no-op, it explicitly tells you the registration already happened.

In practice you'll handle this by checking isRegistered before calling registerVault, or by catching this specific error and proceeding.


VaultBelongsToDifferentInvestor(address vault, string vaultInvestorId)

error VaultBelongsToDifferentInvestor(address vault, string vaultInvestorId);

Thrown by registerVault when the vault address resolves to a non-empty investor ID in the registry, but that ID is not the same as the investor's. Hard stop on cross-investor vault reuse.

Recovery. Use a different vault address. Cross-investor custody sharing is not allowed in this design.


InvalidAddress()

error InvalidAddress();

Thrown wherever the contract guards against the zero address — including registerVault (vault or investor zero), addOperator / removeOperator (target zero), and initialize (token zero).

Recovery. Pass a non-zero address.


NotImplemented()

error NotImplemented();

Thrown by unregisterVault. This is the only thing that function does today — there is no admin-only escape hatch.

Recovery. Redesign your off-ramp. The DS Registry binding survives the lifecycle. The only revocation primitive available to operators or investors is invalidateOperatorPermission(operator), which blocks future registrations but does not unbind anything.


NotAnOperator(address account)

error NotAnOperator(address account);

Declared in Errors.sol with the intent of guarding invalidateOperatorPermission(operator) against arbitrary target addresses, but the live implementation does not enforce it. Calling invalidateOperatorPermission(anyAddress) will succeed and increment the per-pair nonce regardless of whether the target holds OPERATOR_ROLE.

Treat this error as "ABI-declared, runtime-inert" until a future version wires the check.


Inherited errors (OpenZeppelin)

EnforcedPause()

Thrown by registerVault (because of the whenNotPaused modifier) when the contract is paused. Wait for an admin to call unpause().

ExpectedPause()

Thrown by unpause() when the contract is not currently paused. Operationally rare.

AccessControlUnauthorizedAccount(address account, bytes32 neededRole)

Thrown by addOperator, removeOperator, pause, unpause, _authorizeUpgrade when the caller does not hold DEFAULT_ADMIN_ROLE.

ECDSAInvalidSignature*

A family of OpenZeppelin ECDSA errors that may surface from inside SignatureChecker if the signature blob is malformed (wrong length, invalid v, invalid s). In practice you'll see InvalidInvestorSignature() first because the wrapper catches recovery failures, but a malformed-blob revert can still bubble.

Initializable: contract is already initialized

Thrown if anything tries to call initialize() a second time. UUPS-only concern; users do not call initialize directly.