nautechsystems/nautilus_trader · error

Invalid contract type '{}' for {}: expected 'linear' or 'inv

Error message

Invalid contract type '{}' for {}: expected 'linear' or 'inverse'

What it means

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.

Source

Thrown at crates/adapters/okx/src/common/parse.rs:2166

        anyhow::anyhow!(
            "Invalid underlying '{}' for {}: expected format 'BASE-QUOTE'",
            definition.uly,
            definition.inst_id
        )
    })?;

    let instrument_id = parse_instrument_id(definition.inst_id);
    let raw_symbol = Symbol::from_ustr_unchecked(definition.inst_id);
    let base_currency = Currency::get_or_create_crypto_with_context(base_currency, Some(&context));
    let quote_currency =
        Currency::get_or_create_crypto_with_context(quote_currency, Some(&context));
    let settlement_currency =
        Currency::get_or_create_crypto_with_context(definition.settle_ccy, Some(&context));
    let is_inverse = match definition.ct_type {
        OKXContractType::Linear => false,
        OKXContractType::Inverse => true,
        OKXContractType::None => {
            anyhow::bail!(
                "Invalid contract type '{}' for {}: expected 'linear' or 'inverse'",
                definition.ct_type,
                definition.inst_id
            )
        }
    };

    if definition.tick_sz.is_empty() {
        anyhow::bail!("`tick_sz` is empty for {}", definition.inst_id);
    }

    let price_increment = Price::from_str(&definition.tick_sz).map_err(|e| {
        anyhow::anyhow!(
            "Failed to parse `tick_sz` '{}' into Price for {}: {e}",
            definition.tick_sz,
            definition.inst_id
        )
    })?;

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Check the raw OKX /api/v5/public/instruments?instType=SWAP payload for the offending inst_id and confirm what ctType value OKX actually returns
  2. 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
  3. Fix the code or fixture that constructs the instrument definition so ct_type is always Linear or Inverse for SWAP instruments
  4. If the instrument is genuinely unusable, filter it out of the instruments response before parsing

Example fix

// before
OKXContractType::None => {
    anyhow::bail!("Invalid contract type '{}' for {}: expected 'linear' or 'inverse'", definition.ct_type, definition.inst_id)
}
// after
// map the new OKX ctType in the deserializer so it never reaches None:
// "SWAP" new variant => OKXContractType::Linear, or filter unknown ctTypes upstream
Defensive patterns

Strategy: validation

Validate before calling

fn valid_swap(def: &OKXInstrumentDef) -> bool { matches!(def.ct_type, OKXContractType::Linear | OKXContractType::Inverse) }
if !valid_swap(&definition) { skip_or_log(&definition.inst_id); }

Type guard

fn has_contract_type(def: &OKXInstrumentDef) -> bool { !matches!(def.ct_type, OKXContractType::None) }

Try / catch

match parse_instrument_any(&def) {
    Err(e) if e.to_string().contains("Invalid contract type") => { log::warn!("skipping {}: {e}", def.inst_id); }
    Err(e) => return Err(e),
    Ok(inst) => instruments.push(inst),
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.

Related errors


AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08). Data as JSON: /api/errors/09dc9d26d48a053a. Report an issue: GitHub.