nautechsystems/nautilus_trader · error

invalid Bybit TP/SL order type: '{s}', expected Market or Li

Error message

invalid Bybit TP/SL order type: '{s}', expected Market or Limit

What it means

When parsing Bybit TP/SL parameters, the adapter maps the order type attached to TP/SL orders to BybitOrderType. Only "Market" and "Limit" are defined by Bybit for TP/SL orders; any other string is rejected explicitly instead of falling through serde's lenient deserialization.

Source

Thrown at crates/adapters/bybit/src/common/parse.rs:1969

    Ok(result)
}

pub(crate) fn parse_trigger_type(s: &str) -> anyhow::Result<BybitTriggerType> {
    match s {
        "LastPrice" => Ok(BybitTriggerType::LastPrice),
        "MarkPrice" => Ok(BybitTriggerType::MarkPrice),
        "IndexPrice" => Ok(BybitTriggerType::IndexPrice),
        _ => anyhow::bail!(
            "invalid Bybit trigger type: '{s}', expected LastPrice, MarkPrice, or IndexPrice"
        ),
    }
}

pub(crate) fn parse_tp_sl_order_type(s: &str) -> anyhow::Result<BybitOrderType> {
    match s {
        "Market" => Ok(BybitOrderType::Market),
        "Limit" => Ok(BybitOrderType::Limit),
        _ => anyhow::bail!("invalid Bybit TP/SL order type: '{s}', expected Market or Limit"),
    }
}

// A plain `serde_json` deserialize would accept unknown strings: `BybitTpSlMode` carries a
// `#[serde(other)] Unknown` variant, so garbage would silently map to `Unknown`.
pub(crate) fn parse_tpsl_mode(s: &str) -> anyhow::Result<BybitTpSlMode> {
    match s {
        "Full" => Ok(BybitTpSlMode::Full),
        "Partial" => Ok(BybitTpSlMode::Partial),
        _ => anyhow::bail!("invalid Bybit TP/SL mode: '{s}', expected Full or Partial"),
    }
}

#[cfg(test)]
mod tests {
    use nautilus_model::{
        data::BarSpecification,
        enums::{AggregationSource, BarAggregation, PositionSide, PriceType},

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Inspect the raw response's orderType field and correct it to "Market" or "Limit"
  2. Update the adapter/dependency to a version supporting the new order type
  3. Fix fixture or mock data casing to match Bybit's exact strings

Example fix

// before
parse_tp_sl_order_type("market")? // rejected
// after
parse_tp_sl_order_type("Market")?
Defensive patterns

Strategy: validation

Validate before calling

fn is_valid_tpsl_order_type(s: &str) -> bool { matches!(s, "Market" | "Limit") }

Type guard

fn valid_order_type(s: &str) -> Option<BybitOrderType> {
    match s { "Market" => Some(BybitOrderType::Market), "Limit" => Some(BybitOrderType::Limit), _ => None }
}

Try / catch

let ot = parse_tp_sl_order_type(raw).unwrap_or(BybitOrderType::Market); // or surface the error

Prevention

When it happens

Trigger: parse_bybit_tp_sl_params receives a payload where the TP/SL order type field (e.g. orderType in the TPSL response) is not exactly "Market" or "Limit" — typos, lowercase 'market', or an exchange-side new value.

Common situations: Mock/test fixtures with wrong casing; a newer Bybit API adding an order type the adapter doesn't know; manually constructed JSON used in integration testing.

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/db720bce7dba5d1d. Report an issue: GitHub.