nautechsystems/nautilus_trader · error · anyhow::Error

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

Error message

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

What it means

parse_bybit_tp_sl_params requires 'bbo_side_type' to be a string because it is subsequently parsed into the BybitBboSideType enum. If the value exists but is not a string, the parser bails early rather than attempting coercions.

Source

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

        })?;
        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();

    if has_bbo_side_type != has_bbo_level {
        anyhow::bail!("'bbo_side_type' and 'bbo_level' must be provided together");
    }

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

    if let Some(value) = params.get("bbo_level") {
        let level = if let Some(s) = value.as_str() {
            s.to_string()
        } else if let Some(i) = value.as_i64() {
            i.to_string()
        } else if let Some(u) = value.as_u64() {
            u.to_string()
        } else {
            anyhow::bail!("invalid type for 'bbo_level': {value}, expected string or integer");
        };
        result.bbo_level = Some(parse_bbo_level(level)?);
    }

    Ok(result)

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Pass bbo_side_type as a string (the expected enum representation)
  2. Coerce the config value with .to_string()/as_str before inserting it
  3. Also ensure bbo_level is supplied together with bbo_side_type, or you hit the pairing error next

Example fix

// before
let params = serde_json::json!({"bbo_side_type": 2, "bbo_level": 1});
// after
let params = serde_json::json!({"bbo_side_type": "Spot", "bbo_level": 1});
Defensive patterns

Strategy: validation

Validate before calling

if let Some(v) = params.get("bbo_side_type") {
    anyhow::ensure!(v.is_string(), "bbo_side_type must be a string");
    anyhow::ensure!(params.contains_key("bbo_level"), "bbo_level required with bbo_side_type");
}

Type guard

fn is_valid_bbo_side_type(v: &serde_json::Value) -> bool {
    v.as_str().is_some()
}

Try / catch

match parse_bybit_tp_sl_params(&params) {
    Ok(p) => p,
    Err(e) if e.to_string().contains("bbo") => {
        eprintln!("invalid BBO params: {e}");
        return Err(e);
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Passing params["bbo_side_type"] as a non-string value (number, bool, object) in the Bybit TP/SL params map.

Common situations: Programmatic params construction or config deserialization where bbo_side_type lost its string type; also forgetting that bbo_side_type must be paired with bbo_level.

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