Skip to main content

Error codes

Look up a revert string here to find the contract that raises it and the condition behind it. Each table maps the revert strings of one contract to their causes. The tables cover the core, DAO, and net-pressure contracts of yb-core. They also cover the lending oracles YBLendingOracle, YBLendingOracleLL, and YBLendingOracleLLFactory, the Curve Twocrypto pools of markets 7 to 10, and the pool policies.

The four ERC-4626 cap strings share one row in the LiquidityGauge.vy and FastGauge.vy tables, written as erc4626: deposit/mint/withdraw/redeem more than maximum.

Some checks have no message and revert with empty data. For each contract that has them, a list after its table names the main ones.

A revert inside a Curve pool call reaches the caller of the YieldBasis function. For example, a pool policy revert inside add_liquidity reaches the caller of LT.deposit.

LT.deposit, LT.withdraw, and AMM.exchange end with the borrower-fee donation to the Curve pool. If that donation fails, the pool reverts with slippage or donation above cap!, and the whole call reverts. A VirtualPool route reverts in the same way, through AMM.exchange. For the donation, read distribute_borrower_fees in the LT contract reference.

Core​

LT.vy​

LT.vy reverts with these strings:

StringCause
AccessCaller neither admin nor factory admin
Already setSecond set_amm
Slippagemin_shares / min_assets not met
Debt too highLEVAMM value after the deposit would exceed AMM.max_debt() / 2 (preview_deposit also raises it)
Withdrawing nothingwithdraw(0)
Withdraw to/from stakerThe gauge (staker) is msg.sender or receiver
We're dead. Use emergency_withdrawNormal withdraw while the LEVAMM is killed
Remainder too smallSupply would drop below MIN_SHARE_REMAINDER (1e6)
Deposit to stakerreceiver == staker in deposit
N>2Constructor: the Curve pool has more than 2 coins (coins(2) did not revert)
receiveremergency_withdraw from the emergency admin or the admin with receiver other than owner
owneremergency_withdraw from any other caller with owner other than msg.sender
Not killedA caller other than the position owner calls emergency_withdraw while the LEVAMM is live
Need factorywithdraw_admin_fees called when admin is not a factory contract
KilledAdmin-fee withdrawal while the LEVAMM is killed
No fee_receiverFactory fee receiver is unset
Staker=fee_receiverThe fee receiver is the gauge (staker)
Loss made admin fee negativeAdmin-fee withdrawal attempted while admin bucket is negative
Staker already setSecond set_staker
GASGauge checkpoint retry path: _checkpoint_gauge falls back to a fixed-gas call (200k) and requires the caller to supply at least that much. This guards against gas-griefing of reward checkpoints

For the caller rules of emergency_withdraw, read emergency_withdraw() in the LT contract reference.

AMM.vy​

AMM.vy, the LEVAMM contract, reverts with these strings:

StringCause
Fee too highConstructor: fee > MAX_FEE (10%)
Unsafe mindebt < coll_value · MIN_SAFE_DEBT / 1e18
Unsafe maxdebt > coll_value · MAX_SAFE_DEBT / 1e18
Accessset_rate / set_fee / set_killed from non-LT
Access violation_deposit / _withdraw from non-LT
Rate too highrate > MAX_RATE (100% APR)
Amount too largeget_dy(i=0, in_amount > debt)
Slippageexchange with out < min_out
Empty AMMexchange with collateral_amount == 0
Bad final statePost-swap x0 < x0_pre

This call reverts without a message:

  • set_fee with fee > MAX_FEE, the same check as Fee too high.

VirtualPool.vy​

VirtualPool.vy reverts with these strings:

StringCause
Wrong calleronFlashLoan not from FACTORY.flash()
SlippageOutput below min_out

These calls revert without a message:

  • onFlashLoan with initiator != self or token != STABLECOIN.
  • get_dy or exchange with (i, j) other than (0, 1) or (1, 0).

Factory.vy​

Factory.vy reverts with these strings:

StringCause
AccessNon-admin on admin function
Wrong stablecoinPool's coin 0 ≠ factory STABLECOIN
Only set onceset_mint_factory re-called
Minterset_allocator targets mint_factory
Already setset_gauge_controller re-called
Admin fee too highmin_admin_fee > 1e18
Bad aggregatorInit / set_agg with agg.price() ∉ (0.9e18, 1.1e18) (open interval, both ends)
Too many marketsmarket_count ≥ 50000
Nonexistent marketfill_staker_vpool(i ≥ market_count)

HybridVault.vy​

HybridVault.vy reverts with these strings:

StringCause
Already initializedA second initialize call
AccessGeneric non-owner caller on owner-only function (deposit, withdraw, emergency_withdraw, stake, unstake, redeem_crvusd, withdraw_scrvusd, recover_tokens)
Only adminset_personal_limit from non-vault-factory-admin
Only ownerOwner-gated call from non-owner (set_crvusd_vault)
Vault not allowedNew crvusd_vault not in the allowlist
Has active positionsset_crvusd_vault while positions exist
Bad pool_idmarket.lt == 0
Not enough crvUSDInsufficient backing without deposit_stablecoins flag
Beyond pool limitDeposit exceeds cap
Debt made too highSanity: debt > 1.1 · additional_crvusd
No liquidity givenlt_shares == 0 from deposit
Cannot withdraw stablesOracle broken, not opting into stablecoin path
Not enough crvUSD leftBacking would drop below requirement
Token not allowedrecover_tokens targets protected token
Oracle is brokenGlobal and per-pool oracle both fail
Zero sharesemergency_withdraw(0)

HybridVaultFactory.vy​

HybridVaultFactory.vy reverts with these strings:

StringCause
Already createdUser already has a vault
Vault not allowedcrvusd_vault not in the allowlist
Vault impl not setvault_impl == 0
AccessNon-ADMIN on admin function
Only vaults can callupdate_* from non-registered vault
Vault wind-downRequirement increase on disallowed vault
Beyond vault limitIncrease exceeds crvusd_vault_limits

HybridFactoryOwner.vy​

HybridFactoryOwner.vy reverts with these strings:

StringCause
AccessCaller is not ADMIN (the YB DAO). For lt_allocate_stablecoins on an enabled LT, the caller is neither ADMIN nor a limit setter
Not enough reservesDeallocation of a disabled LT would drop the allocation below 3/4 of the LEVAMM oracle value

MigrationFactoryOwner.vy​

MigrationFactoryOwner.vy reverts with these strings:

StringCause
AccessCaller is not ADMIN
Not disabledA non-admin calls lt_allocate_stablecoins(lt, 0) on an LT that is not disabled
Not enough reservesDeallocation would drop the allocation below 3/4 of the LEVAMM oracle value

LTMigrator.vy​

LTMigrator.vy reverts with these strings:

StringCause
same LTlt_from and lt_to are the same LT
lt_to deprecatedMigration target is disabled in the factory owner (disabled_lts(lt_to) is true)
lt_from not in factorySource LT is not a factory market
lt_to supersededlt_to is not the newest market for the asset. The deployed LTMigrator can run an older version without this rule; its address is in Contract addresses
not enough outshares_out < min_out in migrate_staked

YBLendingOracle.vy​

YBLendingOracle.vy reverts with these strings:

StringCause
ZeroConstructor: the factory or the proxy implementation is the zero address
No marketcreate_oracles with market_id equal to or above Factory.market_count(), or for a market whose LT address is zero
AMM reentrancyA price read while the market's LEVAMM holds its reentrancy lock (the check_nonreentrant() probe fails)

YBLendingOracleLL.vy​

YBLendingOracleLL.vy reverts with these strings:

StringCause
InitializedSecond initialize
Zeroinitialize with the zero address as LT or factory
ema_timeinitialize or set_ema_time with ema_time 0 or above MAX_EMA_TIME (10**9 s)
Only factoryset_ema_time from an address other than the factory
AMM reentrancyA price read while the market's LEVAMM holds its reentrancy lock

YBLendingOracleLLFactory.vy​

YBLendingOracleLLFactory.vy reverts with these strings:

StringCause
ZeroConstructor with a zero factory, implementation, USD blueprint or DAO address; set_dao with the zero address
ema_timeConstructor or set_default_ema_time with default_ema_time 0 or above MAX_EMA_TIME
No marketcreate_oracles with market_id equal to or above Factory.market_count(), or for a market whose LT address is zero
Not DAOset_dao, set_default_ema_time or set_ema_time from an address other than the DAO
No oraclesset_ema_time for a market that has no oracles

LTSwapZap.vy​

LTSwapZap has no custom revert strings. Its owner-only functions revert with ownable: caller is not the owner (snekmate ownable).

DAO​

YB.vy​

YB.vy reverts with this string:

StringCause
erc20: access is deniedemit not from a minter

VotingEscrow.vy​

VotingEscrow.vy reverts with these strings:

StringCause
Min valuecreate_lock value < UMAXTIME
Withdraw old tokens firstcreate_lock with existing lock
Can only lock until time in the futureunlock_time ≤ block time
Voting lock can be 4 years maxThe new lock end is more than 4 years after the block time
No existing lock foundincrease_amount on empty
Cannot add to expired lock. WithdrawExpired lock
Nothing is lockedincrease_unlock_time on empty
Lock expiredExpired lock
Can only increase lock durationNew end ≤ current end
Not allowedinfinite_lock_toggle blocked by clearance checker
The lock didn't expirewithdraw before expiry
Not supporteddelegate / delegateBySig
erc721: caller is not token owner or approvedTransfer auth fail
Wrong token IDToken ID mismatches owner's address
Need max veLockTransfer with non-max or non-infinite locks
erc721: transfer to non-IERC721Receiver implementersafeTransferFrom to a contract that does not accept the veYB NFT

This call reverts without a message:

  • increase_amount with value < UMAXTIME, the same check as Min value.

GaugeController.vy​

GaugeController.vy reverts with these strings:

StringCause
Gauge already addedDuplicate gauge registration
Gauge not alivecheckpoint, or emit from a gauge, for a gauge that the GaugeController has not added (time_weight(gauge) == 0). Killing a gauge does not raise it
Vote checker not setveYB's transfer_clearance_checker ≠ this
Mismatch in lengthsArray-length mismatch
ExpiredUser's veYB lock expired
Weight too largeSingle weight > 10,000 bp
KilledVoting for killed gauge
Gauge not addedUnregistered gauge (vote_for_gauge_weights or set_killed)
Cannot vote so oftenSame-gauge re-vote within 10 days
Used too much powerSum of veYB vote weights across gauges exceeds 10,000 bp (100%) after this vote

LiquidityGauge.vy​

LiquidityGauge.vy reverts with these strings:

StringCause
YBadd_reward / deposit_reward targets YB
LP_TOKENadd_reward targets LT
Already addedDuplicate add_reward
Not addedchange_reward_distributor for a token that was not added
No rewardsdeposit_reward(0)
Finishes in the pastNew finish time ≤ block time
Rate unknownExtending past old finish time
No rewardReward token has no distributor (it was not added); raised in reward checkpoints
erc4626: deposit/mint/withdraw/redeem more than maximumThe amount of deposit, mint, withdraw, or redeem is above the ERC-4626 maximum for that call, for example maxWithdraw(owner)
erc4626: leave MIN_SHARESGauge supply after the operation would be between 0 and MIN_SHARES (1e12)
ownable: caller is not the ownerOwner-only function from another address (snekmate ownable)
receiverKill-mode redeem with receiver ≠ owner
erc4626: transferFrom operation did not succeedThe LT-share transfer into the gauge failed in deposit or mint
erc4626: transfer operation did not succeedThe LT-share transfer out of the gauge failed in withdraw or redeem

FeeDistributor.vy​

FeeDistributor.vy reverts with these strings:

StringCause
Dedfill_epochs when killed
Might be a vestFee claim routed through a vesting escrow when direct receiver rules do not allow it

InflationaryVest.vy​

InflationaryVest.vy reverts with these strings:

StringCause
Admin requiredstart / set_recipient from non-owner
Already startedSecond start()

VestingEscrow.vy​

VestingEscrow.vy reverts with these strings:

StringCause
Lengths mismatchfund with mismatched arrays
RuggedThe call targets a recipient whose tokens rug_disabled already reclaimed
Disabledfund for a recipient that is disabled
Cannot disabletoggle_disable when can_disable = False
Not disabledrug_disabled for a recipient that is not disabled

CliffEscrow.vy​

CliffEscrow.vy reverts with these strings:

StringCause
Empty recipientinitialize(recipient=0)
Already initializedA second initialize call
Cliff still appliesTransfer/approve before unlock_time
Not authorizedNon-recipient ve op
Cannot recover YBrecover_token(YB)

SnapshotSplitter.vy​

SnapshotSplitter.vy reverts with this string:

StringCause
Already claimedclaim for an address that already claimed

CallComparator.vy​

CallComparator.vy reverts with these strings:

StringCause
Not equalcheck_equal
Equalcheck_nonequal
Not greatercheck_gt
Not lesscheck_lt
Timestamp not greatercheck_timestamp_gt
Timestamp not lesscheck_timestamp_lt
Too earlycheck_called_after rate limit

TokenSender, Multisend, StakeZap​

These contracts have no custom revert strings. A call fails when a token transfer that it makes reverts.

Net pressure​

Owner-only functions of FeeSplitter, MerklPIDDriver, and PID revert with ownable: caller is not the owner (snekmate ownable).

FeeSplitter.vy​

FeeSplitter.vy reverts with this string:

StringCause
fraction > 1Constructor or set_split_fraction with split_fraction above PRECISION (10**18, which is 100%)

MerklPIDDriver.vy​

MerklPIDDriver.vy reverts with these strings:

StringCause
Not managerA function for the owner or the manager (approve_merkl, accept_conditions, create_campaign, override_campaign, set_gains, set_execution_params) from another caller
No wrapperapprove_merkl while no reward wrapper is set
No poolspreview_target_apr while half_tvl is 0

PID.vy​

PID.vy reverts with this string:

StringCause
No poolstrigger or preview_signals while half_tvl is 0

FastGauge.vy​

FastGauge.vy reverts with these strings:

StringCause
Below min supplyGauge supply after the operation would be above 0 and below MIN_TOTAL_SUPPLY (10 × 10**18)
Only PIDset_reward_rate from an address other than pid
ema_timeset_ema_time(0)
No zap: LP is not a pooladd_liquidity, remove_liquidity or remove_liquidity_one_coin while ZAP_ENABLED is false
Bad amounts lengthadd_liquidity with amounts of a length other than POOL_N_COINS
Bad min_amounts lengthremove_liquidity with min_amounts of a length other than POOL_N_COINS
erc4626: deposit/mint/withdraw/redeem more than maximumThe amount of deposit, mint, withdraw, or redeem is above the ERC-4626 maximum for that call, for example maxWithdraw(owner)
erc4626: transferFrom operation did not succeedThe LP-token transfer into the gauge failed in deposit or mint
erc4626: transfer operation did not succeedThe LP-token transfer out of the gauge failed in withdraw or redeem

YBNetPressure.vy​

YBNetPressure.vy reverts with this string:

StringCause
AMM reentrancyA read (net_pressure_oracle, net_pressure_naive, net_pressure_and_tvl, half_tvl_oracle, crvusd_value_fraction) while the market's LEVAMM holds its reentrancy lock

MarketRateGetter.vy​

MarketRateGetter.vy reverts with this string:

StringCause
Bad ssrConstructor: susds.ssr() is below RAY (10**27)

Curve pool (Twocrypto with policy hook)​

These strings come from the Curve Twocrypto v3.0.0 pools of markets 7 to 10. The pools of markets 0 to 6 run older code. To check the version of a pool, call version() on it.

A v3.0.0 pool reverts with these strings:

StringCause
!amountsadd_liquidity with both amounts 0
!wlLP allowlist is on and msg.sender is not on it (not checked for donations)
!initFirst add_liquidity to a pool that is not initialized
nothing mintedadd_liquidity mints 0 LP
nonzero receiverDonation with a receiver other than the zero address
donation above cap!Donation shares would exceed donation_shares_max_ratio of supply
slippageOutput below the caller's minimum in add_liquidity, remove_liquidity, remove_liquidity_fixed_out, remove_liquidity_one_coin or exchange
!coinsexchange_received: the pool did not receive at least dx before the call
same coinexchange with i == j
zero dxexchange with zero input received
negative donationInternal check in tweak_price: boosted virtual price below virtual price
tweaked supply must shrinkInternal check when the pool burns donation shares
!amountremove_liquidity_fixed_out or remove_liquidity_one_coin (and their calc_ views) with more LP than the total supply
!tokensFixed-output or one-coin removal that computes both amounts as 0
!receiverLP-token transfer to the pool itself or to the zero address
!transferFromThe token transfer into the pool failed
!transferThe token transfer out of the pool failed
!balanceAfter the operation, a balance is 0, or the scaled balances differ by 1,000:1 or more
only ownerAdmin function (set_policy_contract, change_allowlist, set_fee_parameters, parameter changes) not from the Curve factory admin

These calls revert without a message:

  • A balanced removal, if the best-effort policy callback fails and less than 500,000 gas remained before the call (assert gas_before_policy_call >= 500_000).
  • An admin call that fails a parameter check.

pool.get_dy and pool.get_dx run in the pool's view contract. They can revert with coin index out of range, do not exchange 0 coins, do not exchange out 0 coins, or unsafe value for y.

Pool policies​

These strings come from the policy contract attached to a Curve pool (pool.POLICY()). For the checks behind them, read Pool policy.

YBOraclizedPolicy​

These strings apply to a pool that runs the oracle-report policy. To see which policy a pool runs, call POLICY() on the pool or read the pool policy table in Contract addresses.

YBOraclizedPolicy reverts with these strings:

StringCause
LT deviationAn LT.deposit debt differs from the proportional debt added_lp * AMM.get_debt() / AMM.collateral_amount() by more than ratio_tolerance. Raised inside the pool's add_liquidity. Compute debt as in Compute debt on a pool that runs the oracle-report policy
auth!update_pool_state caller is not the pool
killeddeliver_oracle_report while the policy is killed
pool uninitializeddeliver_oracle_report, sync or a revive (set_killed(False)) while the pool price_scale is 0. Inside a pool operation, sync and deliver_oracle_report revert on the pool's reentrancy lock instead
report lengthReport is shorter than 112 bytes
signerThe verifier returned the zero address as signer
payload lengthSigned payload is not 41 bytes
payload magicPayload does not start with the Pyth Lazer magic 0x93c7d375
channelPayload channel is not oracle.channel. Also raised by set_oracle_config for a channel outside 1 to 4
one feedPayload has more or less than one feed
feedFeed id is not FEED_ID
price property, exponent property, observation propertyPayload properties are missing or in another order (required: price, exponent, feed update time)
price/exponentPrice mantissa is not positive, or exponent is outside −18 to 0
report timeObservation time is 0, observation is after the message time, or the message time is more than future_window_us after the block
report too oldObservation is more than past_window_us before the block
observation not newerThe stored report (or the watermark after a kill) is at least as new
price boundsReport price is outside oracle.min_price to oracle.max_price. Also raised by set_oracle_config for bounds outside $1e-12 to $1e12 or min_price > max_price
accessset_killed caller is not the owner or the emergency admin
ownable: caller is not the ownerset_policy_config, set_oracle_config or an ownership function from a non-owner
coin index out of rangeget_dy_with_report* with i == j or an index above 1
not attachedget_dy_with_report* while pool.POLICY() is not this policy
unsafe value for yget_dy_with_report* with dx that drains the output coin
base feeset_policy_config: base_fee below 0.1 bp or above fallback_fee
fallback feeset_policy_config: fallback_fee above 10%
captureset_policy_config: capture above 100%
relax timeset_policy_config: relax_time is 0 or above 3,600 s
fast half-lifeset_policy_config: fast half-life below 600 s or above the slow half-life
slow half-lifeset_policy_config: slow half-life above 604,800 s
trend gateset_policy_config: trend_gate above 10%
step capset_policy_config: step_cap is 0 or above 60 bp
report lifetimeset_policy_config: report_lifetime above 14,400 s
ratio toleranceset_policy_config: ratio_tolerance above 10 bp
verifierset_oracle_config: verifier is not a contract
past windowset_oracle_config: past_window_us above 12,000,000
future windowset_oracle_config: future_window_us above 6,000,000
pool=0, feed id, emergency admin=0, amm collateral, amm stablecoin, pool precision, admin feeConstructor checks

The Pyth Pro (Pyth Lazer) verifier can revert deliver_oracle_report with its own errors, for example on a wrong msg.value or a bad signature. The deployed verifier (implementation 0xD8f4A467AbeC64Bc944eee1325B9007879B6555b, PythLazer 0.2.0) has these strings: Insufficient fee provided, input too short, invalid evm magic, invalid signature, invalid signer. input too short and invalid evm magic mean that the update is not in EVM format. invalid signature and invalid signer mean that the signature does not recover to a trusted Pyth signer.

These calls revert without a message:

  • A call with a report longer than 112 bytes, in ABI decoding.
  • deliver_oracle_report on verifier 0.2.0 with a msg.value above verification_fee(), because the refund to the policy fails.

For the exact-fee rule, how a later verifier version differs, and how to check the version, read Verification fee in Oracle report arbitrage.

YBTwocryptoPolicy​

YBTwocryptoPolicy sets only the price_scale target, from the pool's own prices. It reverts with these strings:

StringCause
auth!update_pool_state caller is not the pool
pool=0, fast half-life, slow half-life, half-life order, kappa, deadband, max cap, min capConstructor checks