nautechsystems/nautilus_trader · error
invalid 'position_idx': {idx}, expected 0, 1, or 2
Error message
invalid 'position_idx': {idx}, expected 0, 1, or 2 What it means
The 'position_idx' field parsed from TP/SL update params did not map to 0, 1, or 2, which are the only values Bybit uses for one-way (0) or hedge-mode (1 buy, 2 sell) position indexing; any other integer is rejected by parse_bybit_tp_sl_params.
Source
Thrown at crates/adapters/bybit/src/common/parse.rs:1920
}
}
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();
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") {View on GitHub (pinned to 18893faf8b)
Solutions
- Use only 0, 1, or 2 for 'position_idx'
- Map the desired hedge mode to the correct Bybit index (OneWay=0, BuyHedge=1, SellHedge=2)
- Omit 'position_idx' if the account uses one-way mode and no override is needed
Example fix
// before
params.set("position_idx", 3);
// after
params.set("position_idx", 1); // BuyHedge Defensive patterns
Strategy: validation
Validate before calling
fn validate_position_idx(params: &Params) -> Result<(), String> {
if let Some(v) = params.get("position_idx") {
match v.as_i64() {
Some(i) if (0..=2).contains(&i) => Ok(()),
other => Err(format!("position_idx must be 0, 1, or 2, got {other:?}")),
}
} else { Ok(()) }
} Type guard
fn is_valid_position_idx(v: &Value) -> bool {
matches!(v.as_i64(), Some(0 | 1 | 2))
} Try / catch
match parse_bybit_tp_sl_params(¶ms) {
Ok(p) => submit(p),
Err(e) if e.to_string().contains("invalid 'position_idx'") => {
log::warn("position_idx must be 0 (OneWay), 1 (BuyHedge), or 2 (SellHedge)");
}
Err(e) => return Err(e),
} Prevention
- Use the BybitPositionIdx enum instead of raw integers where possible
- Remember Bybit indices are 0-based: 0=OneWay, 1=BuyHedge, 2=SellHedge
- Clamp/map any exchange-agnostic mode value to a valid Bybit index
When it happens
Trigger: Passing 'position_idx' with a value like 3, -1, or 99 in the TP/SL params.
Common situations: Loop-generated position indices; hardcoded index copied from another exchange's convention; off-by-one where modes were assumed 1-based.
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
- SL override fields require 'stop_loss' to be set
- 'tp_order_type' is 'Limit' but 'tp_limit_price' was not prov
- 'sl_order_type' is 'Limit' but 'sl_limit_price' was not prov
- 'tp_limit_price' requires 'tp_order_type' to be 'Limit'
- 'sl_limit_price' requires 'sl_order_type' to be 'Limit'
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/a098e99b4b242cc9.
Report an issue: GitHub.