{"record":{"id":"df1c838e31f820a2","repo":"nautechsystems/nautilus_trader","slug":"failed-to-parse-tick-sz-into-price-for","errorCode":null,"errorMessage":"Failed to parse `tick_sz` '{}' into Price for {}: {e}","messagePattern":"Failed to parse `tick_sz` '(.+?)' into Price for (.+?): (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/adapters/okx/src/common/parse.rs","lineNumber":1951,"sourceCode":"        common: CommonInstrumentData,\n        margin_fees: MarginAndFees,\n        ts_init: UnixNanos,\n    ) -> anyhow::Result<InstrumentAny>;\n}\n\n/// Extracts common fields shared across all instrument types.\nfn parse_common_instrument_data(\n    definition: &OKXInstrument,\n) -> anyhow::Result<CommonInstrumentData> {\n    let instrument_id = parse_instrument_id(definition.inst_id);\n    let raw_symbol = Symbol::from_ustr_unchecked(definition.inst_id);\n\n    if definition.tick_sz.is_empty() {\n        anyhow::bail!(\"`tick_sz` is empty for {}\", definition.inst_id);\n    }\n\n    let price_increment = Price::from_str(&definition.tick_sz).map_err(|e| {\n        anyhow::anyhow!(\n            \"Failed to parse `tick_sz` '{}' into Price for {}: {e}\",\n            definition.tick_sz,\n            definition.inst_id,\n        )\n    })?;\n\n    if definition.lot_sz.is_empty() {\n        anyhow::bail!(\"`lot_sz` is empty for {}\", definition.inst_id);\n    }\n\n    let size_increment = Quantity::from_str(&definition.lot_sz).map_err(|e| {\n        anyhow::anyhow!(\n            \"Failed to parse `lot_sz` '{}' for {}: {e}\",\n            definition.lot_sz,\n            definition.inst_id,\n        )\n    })?;\n    let lot_size = Some(size_increment);","sourceCodeStart":1933,"sourceCodeEnd":1969,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/okx/src/common/parse.rs#L1933-L1969","documentation":"OKX instrument parsing calls `Price::from_str` on the exchange-reported `tick_sz` field, and it failed to parse into the NautilusTrader `Price` type. The library throws this when an instrument definition carries a tick size value that is present but not a valid decimal price string. Parsing fails hard instead of guessing, since a wrong price increment corrupts order pricing downstream.","triggerScenarios":"Calling parse_common_instrument_data (via parse_instrument_with_parser or parse_event_contract_instrument) with an OkxInstrumentDef whose tick_sz is non-empty but not decimal-parseable (e.g. contains scientific notation, units, spaces, or unusual characters from an API schema change).","commonSituations":"Mocked or hand-crafted instrument JSON in tests; a cached/stale instrument response whose schema changed; OKX introducing a new instrument family with an unexpected tick_sz encoding; manual transcription of tick_sz when building fixtures.","solutions":["Log/inspect the raw definition.tick_sz for the failing inst_id and confirm it is a plain decimal string like \"0.1\".","Verify the value against the live OKX /api/v5/public/instruments response for that instId.","If it comes from a fixture or cache, correct or regenerate the fixture from a fresh OKX response.","If parsing still fails for a valid decimal, check the Price::from_str precision limits and normalize the string (trim whitespace, strip units) before parsing."],"exampleFix":"// before\nlet price_increment = Price::from_str(&definition.tick_sz).map_err(|e| ...)?;\n// after\nlet tick = definition.tick_sz.trim();\nlet price_increment = Price::from_str(tick).map_err(|e| ...)?;","handlingStrategy":"validation","validationCode":"fn is_decimal(s: &str) -> bool {\n    let t = s.trim();\n    !t.is_empty() && t.parse::<f64>().is_ok() && !t.contains(['e', 'E'])\n}\n// guard: assert!(is_decimal(definition.tick_sz), \"bad tick_sz: {}\", definition.tick_sz);","typeGuard":"fn valid_price_str(s: &str) -> Option<&str> {\n    let t = s.trim();\n    if !t.is_empty() && t.parse::<f64>().is_ok() { Some(t) } else { None }\n}","tryCatchPattern":"match parse_instrument_with_parser(&definition, ts_init) {\n    Ok(inst) => inst,\n    Err(e) if e.to_string().contains(\"tick_sz\") => {\n        tracing::warn!(inst_id = %definition.inst_id, \"skipping instrument with bad tick_sz: {e}\");\n        continue;\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Validate instrument fields (tick_sz, lot_sz, min_sz, max_mkt_sz) against a decimal-string check immediately after fetching from OKX.","Always fetch fixtures from real /api/v5/public/instruments responses, never hand-write them.","Trim whitespace from exchange strings before parsing.","Reject scientific notation early; convert to plain decimal before calling the parser."],"tags":["okx","instrument-parsing","price-parse","decimal-format"],"backgroundTag":"invalid-argument-format","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"}