{"record":{"id":"ec9e7a0f76a7a764","repo":"nautechsystems/nautilus_trader","slug":"key-must-be-a-positive-u32","errorCode":null,"errorMessage":"`{key}` must be a positive u32","messagePattern":"`(.+?)` must be a positive u32","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/adapters/hyperliquid/src/data.rs","lineNumber":2041,"sourceCode":"    book\n}\n\n// Reads optional `nSigFigs` / `mantissa` L2 precision controls from\n// `subscribe_params`; bails on non-positive integer values.\npub(crate) fn parse_book_precision_params(\n    params: Option<&Params>,\n) -> anyhow::Result<(Option<u32>, Option<u32>)> {\n    let Some(params) = params else {\n        return Ok((None, None));\n    };\n\n    let read_u32 = |key: &str| -> anyhow::Result<Option<u32>> {\n        match params.get(key) {\n            None => Ok(None),\n            Some(v) => v\n                .as_u64()\n                .and_then(|n| u32::try_from(n).ok())\n                .ok_or_else(|| anyhow::anyhow!(\"`{key}` must be a positive u32\"))\n                .map(Some),\n        }\n    };\n\n    Ok((read_u32(\"n_sig_figs\")?, read_u32(\"mantissa\")?))\n}\n\n// Hyperliquid funds perpetuals hourly, so `interval` is fixed at 60 mins;\n// `time` from the venue marks the end of the funding interval in ms.\npub(crate) fn funding_entry_to_update(\n    entry: &HyperliquidFundingHistoryEntry,\n    instrument_id: InstrumentId,\n) -> FundingRateUpdate {\n    let rate = entry.funding_rate;\n    let ts = UnixNanos::from(entry.time * 1_000_000);\n    FundingRateUpdate::new(instrument_id, rate, Some(60), None, ts, ts)\n}\n","sourceCodeStart":2023,"sourceCodeEnd":2059,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/hyperliquid/src/data.rs#L2023-L2059","documentation":"The price-precision parser reads optional `n_sig_figs` and `mantissa` params from instrument definition params and requires each, when present, to be a JSON number representable as a u32. If the value is missing, not an integer, negative, or exceeds u32 range, it fails with \"`{key}` must be a positive u32\". Note the message says 'positive' but zero also technically parses; the guard exists to keep precision params valid.","triggerScenarios":"Passing params like `{\"n_sig_figs\": -1}`, `{\"mantissa\": 4294967296}`, `{\"n_sig_figs\": 2.5}`, or a string `\"5\"` instead of numeric `5` when configuring Hyperliquid instrument price precision.","commonSituations":"Hand-edited JSON config using strings for numbers; copy-pasted signed values; overflow when a value came from a wider type; typos like `n_sig_figs: \"auto\"`.","solutions":["Ensure the param is a non-negative JSON integer within u32 range (0..=4294967295).","Remove quotes so the value is numeric, not a string.","Omit the key entirely if you want the default (None) behavior.","Validate/clamp the value before building the params map."],"exampleFix":"// before\n{\"n_sig_figs\": \"5\", \"mantissa\": -1}\n// after\n{\"n_sig_figs\": 5, \"mantissa\": null}","handlingStrategy":"validation","validationCode":"fn valid_u32_param(v: &serde_json::Value) -> bool {\n    v.as_u64().map(|n| n <= u32::MAX as u64).unwrap_or(false)\n}\n// before building params:\nfor key in [\"n_sig_figs\", \"mantissa\"] {\n    if let Some(v) = params.get(key) {\n        assert!(valid_u32_param(v), \"{key} must be a positive u32\");\n    }\n}","typeGuard":"fn is_u32(v: &serde_json::Value) -> bool {\n    v.as_u64().map(|n| u32::try_from(n).is_ok()).unwrap_or(false)\n}","tryCatchPattern":null,"preventionTips":["Emit precision params as JSON numbers, never strings.","Clamp or validate values against u32 range at config-load time.","Omit optional keys rather than passing sentinel/placeholder values."],"tags":["validation","config","precision","hyperliquid"],"backgroundTag":"invalid-argument-value","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"}