nautechsystems/nautilus_trader · error
invalid 'bbo_level': '{s}', expected 1, 2, 3, 4, or 5
Error message
invalid 'bbo_level': '{s}', expected 1, 2, 3, 4, or 5 What it means
parse_bbo_level validates the bbo_level parameter, which selects which of the top 5 BBO levels triggers a TP/SL order. Only the string values '1' through '5' are accepted; any other string raises this anyhow error.
Source
Thrown at crates/adapters/bybit/src/common/parse.rs:1775
let Some(value) = Option::<String>::deserialize(d)? else {
return Ok(None);
};
parse_smp_type(&value).map(Some).map_err(D::Error::custom)
}
pub fn parse_bbo_side_type(s: &str) -> anyhow::Result<BybitBboSideType> {
match s.to_ascii_lowercase().as_str() {
"queue" => Ok(BybitBboSideType::Queue),
"counterparty" => Ok(BybitBboSideType::Counterparty),
_ => anyhow::bail!("invalid Bybit bbo_side_type: '{s}', expected Queue or Counterparty"),
}
}
pub fn parse_bbo_level(s: String) -> anyhow::Result<String> {
match s.as_str() {
"1" | "2" | "3" | "4" | "5" => Ok(s),
_ => anyhow::bail!("invalid 'bbo_level': '{s}', expected 1, 2, 3, 4, or 5"),
}
}
/// Parses Bybit TP/SL parameters from an optional params map.
pub fn parse_bybit_tp_sl_params(params: Option<&Params>) -> anyhow::Result<BybitTpSlParams> {
let Some(params) = params else {
return Ok(BybitTpSlParams::default());
};
let mut result = BybitTpSlParams {
is_leverage: params.get_bool("is_leverage").unwrap_or(false),
..Default::default()
};
if let Some(s) = get_price_str(params, "take_profit") {
let p =
Price::from_str(&s).map_err(|e| anyhow::anyhow!("invalid 'take_profit' price: {e}"))?;
View on GitHub (pinned to 18893faf8b)
Solutions
- Pass bbo_level as the string '1', '2', '3', '4' or '5'
- Clamp your desired level to the 1-5 range before submitting
- Omit bbo_level to use the default
Example fix
// before
params.insert("bbo_level", "0");
// after
params.insert("bbo_level", "1"); Defensive patterns
Strategy: validation
Validate before calling
assert bbo_level in ("1", "2", "3", "4", "5"), f"bbo_level must be 1-5, got {bbo_level}" Type guard
fn is_valid_bbo_level(s: &str) -> bool {
matches!(s.as_str(), "1" | "2" | "3" | "4" | "5")
} Try / catch
match parse_bbo_level(s) {
Ok(l) => use(l),
Err(e) => { log::error!("{e}"); use_default_level() }
} Prevention
- Remember levels are 1-based and capped at 5
- Pass as a string, not an int, if constructing params manually
- Clamp user input to 1..=5 before submitting
When it happens
Trigger: Passing bbo_level in TP/SL params (via parse_bybit_tp_sl_params, reached from order submission) as e.g. '0', '6', 'level3', or an integer-typed value stringified unusually.
Common situations: Assuming the level range starts at 0 (programmer habit), requesting a level beyond Bybit's supported top-5 depth, or passing an int instead of a string in Python params.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- invalid Bybit bbo_side_type: '{s}', expected Queue or Counte
- TP override fields require 'take_profit' to be set
- invalid 'take_profit' price: '{s}', expected a non-negative
- invalid 'stop_loss' price: '{s}', expected a non-negative va
- invalid price for '{key}': '{s}', expected a finite non-nega
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/0794e919df2d1ae2.
Report an issue: GitHub.