nautechsystems/nautilus_trader · error · anyhow::Error
invalid negative kline trade count {}
Error message
invalid negative kline trade count {} What it means
When decoding Binance Spot klines received over the SBE (binary) market-data channel, the per-kline trade count is carried as a signed i64 and converted to u64 for NautilusTrader's Bar. A negative value cannot occur in a well-formed feed, so this error signals malformed or desynchronized SBE decoding — most often a schema/version mismatch between the adapter's generated codecs and the venue's current SBE templates, or a misaligned buffer.
Source
Thrown at crates/adapters/binance/src/common/parse.rs:1381
let volume_mantissa = i128::from_le_bytes(kline.volume);
let volume_dec =
Decimal::from_i128_with_scale(volume_mantissa, (-klines.qty_exponent as i32) as u32);
let volume = Quantity::from_decimal_dp(volume_dec, size_precision)?;
let quote_volume = Decimal::from_i128_with_scale(
i128::from_le_bytes(kline.quote_volume),
(-klines.price_exponent as i32) as u32,
);
let taker_buy_base_volume = Decimal::from_i128_with_scale(
i128::from_le_bytes(kline.taker_buy_base_volume),
(-klines.qty_exponent as i32) as u32,
);
let taker_buy_quote_volume = Decimal::from_i128_with_scale(
i128::from_le_bytes(kline.taker_buy_quote_volume),
(-klines.price_exponent as i32) as u32,
);
let count = u64::try_from(kline.num_trades).map_err(|_| {
anyhow::anyhow!("invalid negative kline trade count {}", kline.num_trades)
})?;
let ts_event = parse_micros(kline.close_time, "Spot SBE kline close time")?;
let bar = crate::common::bar::BinanceBar::new(
bar_type,
open,
high,
low,
close,
volume,
quote_volume,
count,
taker_buy_base_volume,
taker_buy_quote_volume,
ts_event,
ts_init,
);
bars.push(bar);View on GitHub (pinned to a4b06ed870)
Solutions
- Update NautilusTrader to the latest release so the bundled SBE codecs match the venue's current schema.
- Unsubscribe and resubscribe (or reconnect) the SBE stream to re-sync decoding from a message boundary.
- If it persists, capture the raw frame and symbol/interval and report it to the adapter maintainers.
- As a workaround, configure the data client to use the JSON market-data channel instead of SBE.
Defensive patterns
Strategy: try-catch
Validate before calling
fn kline_trade_count_is_valid(num_trades: i64) -> bool {
num_trades >= 0
} Type guard
fn has_valid_trade_count(num_trades: i64) -> bool {
num_trades >= 0
} Try / catch
// where decoded SBE klines are consumed
let count = match u64::try_from(kline.num_trades) {
Ok(c) => c,
Err(_) => {
tracing::error!(num_trades = kline.num_trades, "malformed SBE kline, resubscribing");
resubscribe_kline_stream();
continue;
}
}; Prevention
- Keep the adapter updated so bundled SBE codecs track the venue's current schema version.
- Resubscribe/reconnect the SBE stream on decode anomalies instead of continuing a desynchronized decoder.
- Capture raw frames when the error recurs and attach symbol/interval when reporting.
- Fall back to the JSON market-data channel for critical pipelines if SBE instability persists.
When it happens
Trigger: Subscribing to Spot SBE kline streams (or replaying captured SBE frames) where the decoded numTrades is negative — typically after Binance updates its SBE schema while the adapter build predates it, or when a recorded byte stream is fed at the wrong offset.
Common situations: Venue ships an SBE template revision; a cached/recorded binary stream is replayed against a different adapter version; stream resubscription after a mid-session protocol switch desynchronizes the decoder.
Related errors
- Symbol '{}' is not trading (status: {})
- Invalid tickSize of 0
- Invalid stepSize of 0
- Unsupported second interval: {step}s
- Unsupported minute interval: {step}m
AI-assisted analysis of nautechsystems/nautilus_trader@a4b06ed870 (2026-08-16).
Data as JSON: /api/errors/b07b5443617b3ded.
Report an issue: GitHub.