nautechsystems/nautilus_trader · error · anyhow::Error
`{key}` must be a positive u32
Error message
`{key}` must be a positive u32 What it means
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.
Source
Thrown at crates/adapters/hyperliquid/src/data.rs:2041
book
}
// Reads optional `nSigFigs` / `mantissa` L2 precision controls from
// `subscribe_params`; bails on non-positive integer values.
pub(crate) fn parse_book_precision_params(
params: Option<&Params>,
) -> anyhow::Result<(Option<u32>, Option<u32>)> {
let Some(params) = params else {
return Ok((None, None));
};
let read_u32 = |key: &str| -> anyhow::Result<Option<u32>> {
match params.get(key) {
None => Ok(None),
Some(v) => v
.as_u64()
.and_then(|n| u32::try_from(n).ok())
.ok_or_else(|| anyhow::anyhow!("`{key}` must be a positive u32"))
.map(Some),
}
};
Ok((read_u32("n_sig_figs")?, read_u32("mantissa")?))
}
// Hyperliquid funds perpetuals hourly, so `interval` is fixed at 60 mins;
// `time` from the venue marks the end of the funding interval in ms.
pub(crate) fn funding_entry_to_update(
entry: &HyperliquidFundingHistoryEntry,
instrument_id: InstrumentId,
) -> FundingRateUpdate {
let rate = entry.funding_rate;
let ts = UnixNanos::from(entry.time * 1_000_000);
FundingRateUpdate::new(instrument_id, rate, Some(60), None, ts, ts)
}
View on GitHub (pinned to 18893faf8b)
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.
Example fix
// before
{"n_sig_figs": "5", "mantissa": -1}
// after
{"n_sig_figs": 5, "mantissa": null} Defensive patterns
Strategy: validation
Validate before calling
fn valid_u32_param(v: &serde_json::Value) -> bool {
v.as_u64().map(|n| n <= u32::MAX as u64).unwrap_or(false)
}
// before building params:
for key in ["n_sig_figs", "mantissa"] {
if let Some(v) = params.get(key) {
assert!(valid_u32_param(v), "{key} must be a positive u32");
}
} Type guard
fn is_u32(v: &serde_json::Value) -> bool {
v.as_u64().map(|n| u32::try_from(n).is_ok()).unwrap_or(false)
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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"`.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- metadata['user'] must not contain surrounding whitespace
- request_rate_per_second must be greater than zero
- order_request_rate_per_second must be greater than zero
- heartbeat_secs must be positive when set
- heartbeat_timeout_secs must cover at least two server heartb
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/ec9e7a0f76a7a764.
Report an issue: GitHub.