What fails closed, and what keeps working
No user funds move during an oracle disruption. The exposure is that liquidations pause on the affected markets, so a position that becomes unhealthy while the feed is down cannot be liquidated until pricing is restored.
Triage
Identify which of these you have before changing anything.Feed nature and staleness budgets
This is the subtlest failure in the system and the one most likely to recur when listing a new asset. A feed has aFeedNature of either Market or Fundamental, and that choice changes how staleness is judged.
- A push feed triggered by deviation only publishes when the price moves past a threshold. If the price is flat, no push is due, and the feed legitimately ages. Age is not a proxy for error on a deviation feed. A stale value means the price has not moved.
- A heartbeat feed publishes on a fixed cadence. Age past that cadence genuinely indicates a problem.
- The leg’s own budget,
max_stale_seconds. Must cover the feed’s slowest legitimate publication interval. - The leg-age spread bound,
MAX_LEG_AGE_SPREAD_SECONDS(3600s). This applies only when both legs areMarketnature. It exists because the blend weights both legs equally, so a market leg far older than its market partner would drag the midpoint while each still satisfies its own budget. AFundamentalleg is exempt, because it prices a slow-moving quantity and its own bound is the intended budget.
max_stale_seconds alone does not fix a slow feed paired with a fast one. If both legs are Market, the 3600s spread bound still applies, and a 12-hour-heartbeat feed can never stay within an hour of a five-minute Reflector leg. Marking the slow leg Fundamental is what turns the spread bound off.
Keep the leg budget below the reference’s own max_price_stale_seconds so the leg budget binds first and the failure points at the leg that caused it.
Blast radius
compose() reads the first source, and propagates a nested error before it reads the second. A failing reference therefore takes every dependent asset with it, even when those assets have a healthy standalone leg of their own, because the second source is never reached.
When several assets fail together, look for a shared reference rather than diagnosing each asset separately. Repair the reference and the dependents recover.
After changing an oracle, set_oracle re-probes dependents. A clean re-probe across all dependents is your confirmation that the repair reached them.
Repair
1
Confirm the failure class
Read the error code and the feed timestamps for both legs. Distinguish a feed that is genuinely down from a budget that does not fit the feed.
2
Decide whether the price is wrong or merely old
If the feed is publishing correctly and the value is simply old because the price has not moved, this is a configuration fault. If the feed has stopped publishing, this is a provider fault.
3
For a configuration fault, correct the leg definition
Set the nature and
max_stale_seconds to match the feed’s real publication model. Submit through governance as an oracle update.4
For a provider fault, wait or fail over
The protocol is already refusing to price, which is the safe state. Restore the provider, or move the asset to a different source through governance.
5
Verify the dependents
Confirm
set_oracle re-probed every dependent without reverting, then read a price for each affected market.Do not
- Do not widen sanity bounds or tolerance bands to clear a stale error. Staleness and deviation are separate checks; widening bands does not fix age, and it removes a guard you will want during a real dislocation.
- Do not unpause or force a price to unblock liquidations. A liquidation executed on an untrusted price is worse than a delayed liquidation.
- Do not treat one asset’s failure as isolated until you have checked whether a shared reference is the cause.

