{"record":{"id":"339bab20158d12e3","repo":"nautechsystems/nautilus_trader","slug":"exp-time-is-required-for","errorCode":null,"errorMessage":"`exp_time` is required for {}","messagePattern":"`exp_time` is required for (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/adapters/okx/src/common/parse.rs","lineNumber":2310,"sourceCode":"    let settlement_currency =\n        Currency::get_or_create_crypto_with_context(definition.settle_ccy, Some(&context));\n    let is_inverse = match definition.ct_type {\n        OKXContractType::Linear => false,\n        OKXContractType::Inverse => true,\n        OKXContractType::None => {\n            anyhow::bail!(\n                \"Invalid contract type '{}' for {}: expected 'linear' or 'inverse'\",\n                definition.ct_type,\n                definition.inst_id\n            )\n        }\n    };\n    let listing_time = definition\n        .list_time\n        .ok_or_else(|| anyhow::anyhow!(\"`list_time` is required for {}\", definition.inst_id))?;\n    let expiry_time = definition\n        .exp_time\n        .ok_or_else(|| anyhow::anyhow!(\"`exp_time` is required for {}\", definition.inst_id))?;\n    let activation_ns = parse_millisecond_timestamp(listing_time);\n    let expiration_ns = parse_millisecond_timestamp(expiry_time);\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` '{}' 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    }","sourceCodeStart":2292,"sourceCodeEnd":2328,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/okx/src/common/parse.rs#L2292-L2328","documentation":"Thrown by `parse_futures_instrument` when the OKX instrument definition has no `exp_time` (expiry timestamp). The expiry is mandatory because the parser computes the instrument's `expiration_ns` from it; without it, no valid Nautilus futures instrument can be constructed. The error names the offending `inst_id`.","triggerScenarios":"Calling `parse_instrument_any`/`parse_futures_instrument` with a definition whose `exp_time` is None — OKX omitted the field in the instruments response or the caller built the struct without it.","commonSituations":"Perpetual swaps or some OKX instrument records where `exp_time` is absent; hand-built definitions in tests/fixtures; API version changes that made `exp_time` optional on the wire.","solutions":["Inspect the OKX instruments response for the inst_id and confirm `exp_time` is present; skip instruments that lack it.","Set `exp_time: Some(<ms timestamp>)` when constructing the definition manually.","For perpetual swaps without expiry, use an adapter/parser path that models them with a far-future or sentinel expiration instead of requiring exp_time."],"exampleFix":"// before\nOkxInstrumentDef { inst_id: \"BTC-USD-240329\", exp_time: None, .. }\n// after\nOkxInstrumentDef { inst_id: \"BTC-USD-240329\", exp_time: Some(1711665600000), .. }","handlingStrategy":"validation","validationCode":"if definition.exp_time.is_none() {\n    eprintln!(\"skipping {}: no exp_time\", definition.inst_id);\n    return Ok(None);\n}","typeGuard":"fn has_exp_time(def: &OkxInstrumentDef) -> bool {\n    def.exp_time.is_some()\n}","tryCatchPattern":"match parse_instrument_any(definition) {\n    Ok(inst) => inst,\n    Err(e) if e.to_string().contains(\"`exp_time` is required\") => { skip; }\n    Err(e) => return Err(e),\n}","preventionTips":["Check exp_time presence for non-perpetual futures before parsing.","Handle perpetuals via a path that does not require exp_time.","Log skipped instruments for review instead of failing the whole load."],"tags":["okx","instrument-parsing","missing-field","futures"],"backgroundTag":"missing-required-argument","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"}