nautechsystems/nautilus_trader · error · anyhow::Error

expected 2 impact prices, received

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.

Solutions

  1. Inspect the raw payload for the offending entry and confirm its impactPrices shape; update parsing if the API changed.
  2. Update/upgrade the adapter to a version matching the current Hyperliquid API schema (or vice versa: pin the API behavior).
  3. Handle entries with missing impact prices gracefully by skipping them or logging a warning instead of treating them as fatal, if the domain allows.
  4. 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

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


AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08). Data as JSON: /api/errors/06bc2175f102b1aa. Report an issue: GitHub.

Appendix: 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)