nautechsystems/nautilus_trader · error
Invalid levels: {}
Error message
Invalid levels: {} What it means
Depth10StreamIterator::next returns this error when self.levels holds a value other than 5 or 25 inside the iterator loop. In normal use Depth10StreamIterator::new already guarantees levels is 5 or 25 via anyhow::ensure!, so reaching this arm indicates the iterator was constructed directly (bypassing new) or an internal invariant was broken.
Source
Thrown at crates/adapters/tardis/src/csv/stream.rs:1528
return Some(Ok(chunk));
}
self.buffer.clear();
let mut records_read = 0;
while records_read < self.chunk_size {
match self.reader.read_record(&mut self.record) {
Ok(true) => {
let result = match self.levels {
5 => self
.record
.deserialize::<TardisOrderBookSnapshot5Record>(None)
.map(|data| self.process_snapshot5(&data)),
25 => self
.record
.deserialize::<TardisOrderBookSnapshot25Record>(None)
.map(|data| self.process_snapshot25(&data)),
_ => return Some(Err(anyhow::anyhow!("Invalid levels: {}", self.levels))),
};
match result {
Ok(depth) => {
self.buffer.push(depth);
records_read += 1;
self.records_processed += 1;
if let Some(limit) = self.limit
&& self.records_processed >= limit
{
break;
}
}
Err(e) => {
return Some(Err(anyhow::anyhow!("Failed to deserialize record: {e}")));
}
}View on GitHub (pinned to 18893faf8b)
Solutions
- Always construct the iterator via Depth10StreamIterator::new (or the public stream_order_book_depth10) which validates levels.
- If you instantiate directly, ensure levels == 5 || levels == 25 before use.
- Do not mutate levels after construction; it is a fixed stream property.
- If you hit this in library code, file a bug — it signals an internal invariant violation.
Example fix
// before: direct struct construction bypassing validation
let it = Depth10StreamIterator { levels: 10, .. };
// after: go through new(), which ensures levels is 5 or 25
let it = Depth10StreamIterator::new(path, chunk, 10, None, None, None, None)?;
// -> returns "Invalid levels: 10. Must be 5 or 25." early, before streaming Defensive patterns
Strategy: type-guard
Validate before calling
// library users never reach this arm; guard construction path instead
let levels = if file_is_snapshot25 { 25 } else { 5 };
anymore::ensure!(levels == 5 || levels == 25); Type guard
fn is_supported_levels(levels: u8) -> bool { levels == 5 || levels == 25 } Try / catch
// the iterator only errors here if built without new(); always use: let it = stream_order_book_depth10(path, chunk, levels, None, None, None, None)?; // which validates levels up front
Prevention
- Never construct Depth10StreamIterator directly; use the public stream function.
- Do not mutate the levels field after construction.
- Keep the constructor's anyhow::ensure! check when refactoring.
- Treat this error in library code as a bug report trigger.
When it happens
Trigger: Constructing Depth10StreamIterator with a struct literal instead of new() and a levels field outside {5, 25}; any code path that mutates levels after construction; the match's fall-through arm at line 1528.
Common situations: Internal maintenance or test code instantiating the private iterator directly; refactoring that moved or removed the constructor check.
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 levels: {levels}. Must be 5 or 25.
- invalid Binance Futures order-book depth; valid values are {
- unrecognized side '{side}'
- Binance Spot order-book depth must be between 1 and 5000
- invalid depth {depth}; valid values are {BYBIT_BOOK_DEPTHS:?
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/ac33cd312151ab00.
Report an issue: GitHub.