nautechsystems/nautilus_trader · error · anyhow::Error

invalid type for 'smp_type': {value}, expected string

Error message

invalid type for 'smp_type': {value}, expected string

What it means

parse_bybit_tp_sl_params validates optional 'smp_type' entries in a TP/SL params map. The key was present, but the JSON/serde value is not a string, so the parser refuses to interpret it and bails instead of guessing a coercion. The message interpolates the offending value so the caller can see what was passed.

Source

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

    if let Some(value) = params.get("order_iv") {
        match get_price_str(params, "order_iv") {
            Some(s) => result.order_iv = Some(s),
            None => {
                anyhow::bail!("invalid type for 'order_iv': {value}, expected string or number")
            }
        }
    }

    if let Some(value) = params.get("mmp") {
        match value.as_bool() {
            Some(b) => result.mmp = Some(b),
            None => anyhow::bail!("invalid type for 'mmp': {value}, expected bool"),
        }
    }

    if let Some(value) = params.get("smp_type") {
        let smp_type = value.as_str().ok_or_else(|| {
            anyhow::anyhow!("invalid type for 'smp_type': {value}, expected string")
        })?;
        result.smp_type = Some(parse_smp_type(smp_type)?);
    }

    if let Some(value) = params.get("position_idx") {
        let idx = value.as_i64().ok_or_else(|| {
            anyhow::anyhow!("invalid type for 'position_idx': {value}, expected integer")
        })?;
        result.position_idx = Some(match idx {
            0 => BybitPositionIdx::OneWay,
            1 => BybitPositionIdx::BuyHedge,
            2 => BybitPositionIdx::SellHedge,
            _ => anyhow::bail!("invalid 'position_idx': {idx}, expected 0, 1, or 2"),
        });
    }

    let has_bbo_side_type = params.get("bbo_side_type").is_some();
    let has_bbo_level = params.get("bbo_level").is_some();

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Pass smp_type as a string (e.g. "None", "Cancel") in the params map
  2. Coerce numeric/bool config values to strings before calling parse_bybit_tp_sl_params
  3. Fix the config source (YAML/JSON/env) so smp_type is quoted

Example fix

// before
let params = serde_json::json!({"smp_type": 1});
// after
let params = serde_json::json!({"smp_type": "None"});
Defensive patterns

Strategy: validation

Validate before calling

fn ensure_string_param(params: &serde_json::Value, key: &str) -> anyhow::Result<&str> {
    params.get(key)
        .and_then(|v| v.as_str())
        .ok_or_else(|| anyhow::anyhow!("{key} must be a string"))
}

Type guard

fn as_str_param(v: &serde_json::Value) -> Option<&str> {
    if v.is_string() { v.as_str() } else { None }
}

Try / catch

match parse_bybit_tp_sl_params(&params) {
    Ok(p) => p,
    Err(e) if e.to_string().contains("smp_type") => {
        eprintln!("smp_type must be a string: {e}");
        Default::default()
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Passing params["smp_type"] as a non-string JSON value, e.g. an integer 1, a bool, or a nested object, into the Bybit TP/SL params builder.

Common situations: Building the params map from untyped config files, environment variables, or deserialized JSON where the value was not normalized to a string like "None" before calling the parser.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


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