{"record":{"id":"06bc2175f102b1aa","repo":"nautechsystems/nautilus_trader","slug":"expected-2-impact-prices-received-len","errorCode":null,"errorMessage":"expected 2 impact prices, received {len}","messagePattern":"expected 2 impact prices, received (.+?)","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/adapters/hyperliquid/src/websocket/handler.rs","lineNumber":1331,"sourceCode":"            .mid_px\n            .map(|value| Price::from_decimal(value).map_err(anyhow::Error::msg))\n            .transpose()?;\n        let funding_rate = ctx.funding;\n        let open_interest = ctx.open_interest;\n        let premium = ctx.premium;\n        let day_ntl_volume = ctx.shared.day_ntl_vlm;\n        let day_base_volume = ctx\n            .shared\n            .day_base_vlm\n            .ok_or_else(|| anyhow::anyhow!(\"missing dayBaseVlm\"))?;\n        let impact_prices = match ctx.shared.impact_pxs {\n            Some(values) => match values.as_slice() {\n                [bid, ask] => Some(HyperliquidImpactPrices {\n                    bid: bid.parse::<Price>().map_err(anyhow::Error::msg)?,\n                    ask: ask.parse::<Price>().map_err(anyhow::Error::msg)?,\n                }),\n                other => {\n                    anyhow::bail!(\"expected 2 impact prices, received {}\", other.len());\n                }\n            },\n            None => None,\n        };\n\n        Ok(HyperliquidDexAssetCtx {\n            dex: dex.to_string(),\n            instrument_id,\n            mark_price,\n            oracle_price,\n            prev_day_price,\n            mid_price,\n            impact_prices,\n            funding_rate,\n            open_interest,\n            premium,\n            day_ntl_volume,\n            day_base_volume,","sourceCodeStart":1313,"sourceCodeEnd":1349,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/hyperliquid/src/websocket/handler.rs#L1313-L1349","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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."],"exampleFix":"// before\nother => {\n    anyhow::bail!(\"expected 2 impact prices, received {}\", other.len());\n}\n// after\nother => {\n    tracing::warn!(\"unexpected impactPrices count ({}), skipping entry\", other.len());\n    None\n}","handlingStrategy":"validation","validationCode":"// validate before parsing\nif let Some(v) = entry.get(\"impactPrices\") {\n    let arr = v.as_array().ok_or(\"impactPrices not an array\")?;\n    if arr.len() != 2 {\n        return Err(anyhow!(\"expected 2 impact prices, got {}\", arr.len()));\n    }\n}","typeGuard":"fn is_bid_ask_pair(v: &serde_json::Value) -> Option<&Vec<serde_json::Value>> {\n    v.as_array().filter(|a| a.len() == 2)\n}","tryCatchPattern":null,"preventionTips":["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"],"tags":["hyperliquid","rust","api","response-parsing","websocket"],"backgroundTag":"unexpected-api-response-shape","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}