{"record":{"id":"09dc9d26d48a053a","repo":"nautechsystems/nautilus_trader","slug":"invalid-contract-type-for-expected-linea","errorCode":null,"errorMessage":"Invalid contract type '{}' for {}: expected 'linear' or 'inverse'","messagePattern":"Invalid contract type '(.+?)' for (.+?): expected 'linear' or 'inverse'","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/adapters/okx/src/common/parse.rs","lineNumber":2166,"sourceCode":"        anyhow::anyhow!(\n            \"Invalid underlying '{}' for {}: expected format 'BASE-QUOTE'\",\n            definition.uly,\n            definition.inst_id\n        )\n    })?;\n\n    let instrument_id = parse_instrument_id(definition.inst_id);\n    let raw_symbol = Symbol::from_ustr_unchecked(definition.inst_id);\n    let base_currency = Currency::get_or_create_crypto_with_context(base_currency, Some(&context));\n    let quote_currency =\n        Currency::get_or_create_crypto_with_context(quote_currency, Some(&context));\n    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\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    })?;","sourceCodeStart":2148,"sourceCodeEnd":2184,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/okx/src/common/parse.rs#L2148-L2184","documentation":"OKX swap instruments must declare a contract type of either 'linear' or 'inverse'. This error is raised by parse_swap_instrument when the OKX instruments response contains a SWAP instrument whose ct_type field is missing or mapped to OKXContractType::None, making it impossible to determine settlement direction. The raw ct_type value and inst_id are embedded in the message for diagnosis.","triggerScenarios":"Calling parse_instrument_any (or parse_swap_instrument directly) on an OKX InstrumentsResponse where a SWAP entry has ct_type OKXContractType::None — e.g. OKX returned an empty/unknown ctType string, or the definition was constructed programmatically without setting ct_type.","commonSituations":"OKX adds a new contract type not yet handled by the adapter's ct_type deserializer, falling through to None; mocked/fixture instrument definitions in tests or local replay data built without ctType; API schema drift between OKX v5 versions.","solutions":["Check the raw OKX /api/v5/public/instruments?instType=SWAP payload for the offending inst_id and confirm what ctType value OKX actually returns","Update the OKXContractType deserialization in the adapter to map any new ctType value OKX introduced (e.g. new linear/inverse variants) instead of falling through to None","Fix the code or fixture that constructs the instrument definition so ct_type is always Linear or Inverse for SWAP instruments","If the instrument is genuinely unusable, filter it out of the instruments response before parsing"],"exampleFix":"// before\nOKXContractType::None => {\n    anyhow::bail!(\"Invalid contract type '{}' for {}: expected 'linear' or 'inverse'\", definition.ct_type, definition.inst_id)\n}\n// after\n// map the new OKX ctType in the deserializer so it never reaches None:\n// \"SWAP\" new variant => OKXContractType::Linear, or filter unknown ctTypes upstream","handlingStrategy":"validation","validationCode":"fn valid_swap(def: &OKXInstrumentDef) -> bool { matches!(def.ct_type, OKXContractType::Linear | OKXContractType::Inverse) }\nif !valid_swap(&definition) { skip_or_log(&definition.inst_id); }","typeGuard":"fn has_contract_type(def: &OKXInstrumentDef) -> bool { !matches!(def.ct_type, OKXContractType::None) }","tryCatchPattern":"match parse_instrument_any(&def) {\n    Err(e) if e.to_string().contains(\"Invalid contract type\") => { log::warn!(\"skipping {}: {e}\", def.inst_id); }\n    Err(e) => return Err(e),\n    Ok(inst) => instruments.push(inst),\n}","preventionTips":["Keep the OKXContractType deserializer updated with OKX v5 API changelog entries","Always set ct_type when constructing instrument definitions in fixtures","Log unknown ctType values at deserialize time so drift is detected early"],"tags":["okx","instrument-parsing","contract-type","validation"],"backgroundTag":"invalid-enum-value","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}