nautechsystems/nautilus_trader · error · anyhow::Error
expected 2 impact prices, received {len}
Error message
expected 2 impact prices, received {len} What it means
normalize_all_dex_asset_ctx_entry parses the impactPrices field of a DEX asset context (DEX perp market summary data). The API is expected to return exactly a [bid, ask] pair of strings; any other array length triggers this bail. This guards against malformed or changed upstream payload shapes before they can propagate into Price values.
Source
Thrown at crates/adapters/hyperliquid/src/websocket/handler.rs:1331
.mid_px
.map(|value| Price::from_decimal(value).map_err(anyhow::Error::msg))
.transpose()?;
let funding_rate = ctx.funding;
let open_interest = ctx.open_interest;
let premium = ctx.premium;
let day_ntl_volume = ctx.shared.day_ntl_vlm;
let day_base_volume = ctx
.shared
.day_base_vlm
.ok_or_else(|| anyhow::anyhow!("missing dayBaseVlm"))?;
let impact_prices = match ctx.shared.impact_pxs {
Some(values) => match values.as_slice() {
[bid, ask] => Some(HyperliquidImpactPrices {
bid: bid.parse::<Price>().map_err(anyhow::Error::msg)?,
ask: ask.parse::<Price>().map_err(anyhow::Error::msg)?,
}),
other => {
anyhow::bail!("expected 2 impact prices, received {}", other.len());
}
},
None => None,
};
Ok(HyperliquidDexAssetCtx {
dex: dex.to_string(),
instrument_id,
mark_price,
oracle_price,
prev_day_price,
mid_price,
impact_prices,
funding_rate,
open_interest,
premium,
day_ntl_volume,
day_base_volume,View on GitHub (pinned to 18893faf8b)
Solutions
- Inspect the raw payload for the offending entry and confirm its impactPrices shape; update parsing if the API changed.
- Update/upgrade the adapter to a version matching the current Hyperliquid API schema (or vice versa: pin the API behavior).
- Handle entries with missing impact prices gracefully by skipping them or logging a warning instead of treating them as fatal, if the domain allows.
- Add a payload-shape test/mock against a recorded real response to catch regressions early.
Example fix
// before
other => {
anyhow::bail!("expected 2 impact prices, received {}", other.len());
}
// after
other => {
tracing::warn!("unexpected impactPrices count ({}), skipping entry", other.len());
None
} Defensive patterns
Strategy: validation
Validate before calling
// validate before parsing
if let Some(v) = entry.get("impactPrices") {
let arr = v.as_array().ok_or("impactPrices not an array")?;
if arr.len() != 2 {
return Err(anyhow!("expected 2 impact prices, got {}", arr.len()));
}
} Type guard
fn is_bid_ask_pair(v: &serde_json::Value) -> Option<&Vec<serde_json::Value>> {
v.as_array().filter(|a| a.len() == 2)
} Prevention
- Pin adapter code against the live Hyperliquid API schema and re-verify after API updates
- Snapshot-test parsing with recorded real API responses
- Log raw payloads when normalization fails to speed diagnosis
When it happens
Trigger: Receiving an allMids/DEX asset-context entry whose impactPrices array has 0, 1, 3, or more elements instead of exactly 2. Happens when the Hyperliquid DEX payload deviates from the documented [bid, ask] shape (schema change, partial data, or unexpected entry type).
Common situations: A Hyperliquid API schema change altering impactPrices; an edge-case market entry with missing/empty impact prices; proxy or mocked endpoints returning abbreviated payloads; parsing a payload captured from a different API version.
Understand the failure class
Background: "invalid response format", "malformed payload", "missing data field": when an API returns 200 but the response shape is wrong — this error's family across 23 libraries.
Related errors
- Failed to send subscribe command: {e}
- Not a subscription channel: {kind}
- Hyperliquid allows at most {} WebSocket connections per rout
- Failed to send SetDepth10Sub command: {e}
- Failed to send UpdateTradeSubs command: {e}
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/06bc2175f102b1aa.
Report an issue: GitHub.