{"record":{"id":"380b5a5e59bc8cc5","repo":"nautechsystems/nautilus_trader","slug":"quantity-overflow-for-ticks-ticks-decimals-dec","errorCode":null,"errorMessage":"Quantity overflow for ticks={ticks}, decimals={decimals}: {e}","messagePattern":"Quantity overflow for ticks=(.+?), decimals=(.+?): (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/adapters/lighter/src/common/parse.rs","lineNumber":81,"sourceCode":"/// sign separately before invoking this parser.\n///\n/// Conversion routes through [`Decimal`] and [`Quantity::from_decimal_dp`]\n/// so out-of-range tick counts return an error rather than panicking inside\n/// the unchecked mantissa-exponent constructor.\n///\n/// # Errors\n///\n/// Returns an error if `decimals` exceeds [`MAX_DECIMALS`], if `ticks` is\n/// negative, or if the resulting value exceeds the [`Quantity`] range.\npub fn parse_quantity_from_ticks(ticks: i64, decimals: u8) -> anyhow::Result<Quantity> {\n    anyhow::ensure!(\n        decimals <= MAX_DECIMALS,\n        \"size decimals {decimals} exceeds maximum {MAX_DECIMALS}\",\n    );\n    anyhow::ensure!(ticks >= 0, \"negative tick count {ticks} for Quantity\");\n    let decimal = Decimal::new(ticks, u32::from(decimals));\n    Quantity::from_decimal_dp(decimal, decimals).map_err(|e| {\n        anyhow::anyhow!(\"Quantity overflow for ticks={ticks}, decimals={decimals}: {e}\")\n    })\n}\n\n/// Converts a decimal string into a Nautilus [`Price`] at the requested precision.\n///\n/// # Errors\n///\n/// Returns an error if the string is not a decimal, if `precision` exceeds\n/// [`MAX_DECIMALS`], or if the resulting value is out of range.\npub fn parse_price(value: &str, precision: u8) -> anyhow::Result<Price> {\n    anyhow::ensure!(\n        precision <= MAX_DECIMALS,\n        \"price precision {precision} exceeds maximum {MAX_DECIMALS}\",\n    );\n    let decimal =\n        Decimal::from_str(value).map_err(|e| anyhow::anyhow!(\"invalid price `{value}`: {e}\"))?;\n    Price::from_decimal_dp(decimal, precision)\n        .map_err(|e| anyhow::anyhow!(\"invalid price `{value}` at precision {precision}: {e}\"))","sourceCodeStart":63,"sourceCodeEnd":99,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/lighter/src/common/parse.rs#L63-L99","documentation":"parse_quantity_from_ticks routes the tick count through rust_decimal's Decimal and Quantity::from_decimal_dp so that tick counts too large for Nautilus's fixed-precision Quantity representation return an error instead of panicking inside the unchecked mantissa-exponent constructor. This error means the numeric value implied by ticks=10^-decimals falls outside the Quantity range (roughly 18 significant digits).","triggerScenarios":"Calling parse_quantity_from_ticks with a tick magnitude whose scaled value exceeds the Quantity fixed-point range — e.g. i64::MAX ticks, or large ticks combined with decimals that push the value past ~10^18.","commonSituations":"A malformed or hostile exchange payload with an absurd base_amount; a market whose size_decimals is misreported (far too small), inflating the implied value; unit tests probing the upper bound with i64::MAX.","solutions":["Verify the size_decimals value for the market against the orderBookDetails REST response — a wrong decimals value scales the value incorrectly.","Sanity-check the raw tick count against plausible market maximums before conversion and reject out-of-band values upstream.","Treat the message as a data-integrity problem: log ticks and decimals, drop the update, and flag the source payload rather than retrying.","If you legitimately need larger values, you cannot — Nautilus Quantity is fixed-precision; the value must be capped or the payload rejected."],"exampleFix":"// before\nlet qty = parse_quantity_from_ticks(ticks, decimals)?;\n// after\nif ticks > MAX_PLAUSIBLE_TICKS {\n    return Err(anyhow::anyhow!(\"implausible tick count {ticks} for market\"));\n}\nlet qty = parse_quantity_from_ticks(ticks, decimals)?;","handlingStrategy":"validation","validationCode":"const MAX_PLAUSIBLE_TICKS: i64 = 10_000_000_000_000_000;\nfn ticks_in_range(ticks: i64) -> bool {\n    (0..=MAX_PLAUSIBLE_TICKS).contains(&ticks)\n}","typeGuard":null,"tryCatchPattern":"match parse_quantity_from_ticks(ticks, decimals) {\n    Ok(q) => q,\n    Err(e) if e.to_string().contains(\"Quantity overflow\") => {\n        tracing::error!(%e, \"implausible size; dropping update\");\n        return Ok(()); // skip malformed update\n    }\n    Err(e) => return Err(e.into()),\n}","preventionTips":["Sanity-check tick counts against instrument max quantity before conversion","Verify size_decimals against the orderBookDetails response at subscription time","Treat overflow-sized payloads as data-integrity events: log raw value and flag the source","Do not attempt to 'widen' Quantity — it is fixed-precision by design"],"tags":["rust","parsing","overflow","quantity"],"backgroundTag":"value-out-of-range","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"}