# Liquidation reverts on edge conditions

Liquidation or close paths that revert on zero amounts, strict inequalities, or health checks

_Area: DeFi oracles, liquidations and flash loans. Seen in 3 disclosed reports; latest 2025-06-18. Updated 2026-10-30._

## How it works
Liquidation and close paths must succeed exactly when a position is at its worst. The assumption that breaks is that the post-liquidation state is still "normal": non-zero debt, non-zero risk, and every coupled variable still above its minimum. When a full close drives a value to zero, or a helper leaves an accumulator uninitialized, the health or minimum-borrow check rejects the very state the liquidator needs to reach. The position stays open and unliquidatable.

## Where it shows up
- Health checks that assert on `total_risk`/exposure being non-zero, or that panic on a zero denominator after a full close [E3](https://bugbountylibrary.com/r/c4%3A2025-03-starknet-perpetual%3AM-01.md).
- Minimum-borrow or minimum-size checks applied to the post-liquidation state, especially when a settlement or cash path skips the check that other paths enforce [E1](https://bugbountylibrary.com/r/sherlock%3A2023-03-notional%3AH-9.md).
- Helper or wrapper contracts (invokers, routers) that mirror core logic but initialize accumulators only inside a conditional branch [E2](https://bugbountylibrary.com/r/sherlock%3A2023-09-perennial%3AM-4.md).
- Operations that can only reduce one of several coupled debts per call, while the invariant check validates all of them together [E1](https://bugbountylibrary.com/r/sherlock%3A2023-03-notional%3AH-9.md).
- Strict inequalities (`<` vs `<=`) around zero amounts, and close paths that revert on a zero remaining balance.

## How to review
The invariant: for any position that is liquidatable or deleveragable, a full-close call must succeed and leave the account healthy (or fully closed). Check each terminal state explicitly: zero debt, zero risk, zero remaining collateral, and the minimum-size boundary. For every guard on the liquidation path, ask whether the guard's rejected state is reachable by a legitimate full close. For helper contracts, diff accumulator initialization against the canonical core implementation and confirm the value is set on every path, not only inside a settlement loop [E2](https://bugbountylibrary.com/r/sherlock%3A2023-09-perennial%3AM-4.md). For coupled debts, confirm the check and the per-call reduction operate on the same set of variables [E1](https://bugbountylibrary.com/r/sherlock%3A2023-03-notional%3AH-9.md).

## Variants seen
- Zero-risk panic blocks full liquidation of a deleveragable position [E3](https://bugbountylibrary.com/r/c4%3A2025-03-starknet-perpetual%3AM-01.md).
- Minimum-borrow check skipped in settlement creates sub-minimum debts in two currencies that can never be reduced one at a time [E1](https://bugbountylibrary.com/r/sherlock%3A2023-03-notional%3AH-9.md).
- Uninitialized closable amount in a wrapper makes liquidation revert in the common no-pending-position case [E2](https://bugbountylibrary.com/r/sherlock%3A2023-09-perennial%3AM-4.md).

## Impact and severity
Insolvent positions remain open and cannot be deleveraged, so bad debt accrues to the protocol [E1](https://bugbountylibrary.com/r/sherlock%3A2023-03-notional%3AH-9.md)[E3](https://bugbountylibrary.com/r/c4%3A2025-03-starknet-perpetual%3AM-01.md). Liquidators lose gas and fees on reverting calls, discouraging liquidation and worsening the shortfall [E2](https://bugbountylibrary.com/r/sherlock%3A2023-09-perennial%3AM-4.md). Severity rises when the blocked state is the common case rather than a rare edge, and when no alternate path can reduce the position.

## Evidence
- E1: https://bugbountylibrary.com/r/sherlock%3A2023-03-notional%3AH-9.md
- E2: https://bugbountylibrary.com/r/sherlock%3A2023-09-perennial%3AM-4.md
- E3: https://bugbountylibrary.com/r/c4%3A2025-03-starknet-perpetual%3AM-01.md
