nautechsystems/nautilus_trader · warning
16 lowercase hex bytes are valid TradeId
Error message
16 lowercase hex bytes are valid TradeId
What it means
trade_id_from_hash derives a TradeId from a 64-bit hash by writing 16 lowercase hex characters into a fixed buffer, then asserts via TradeId::from_bytes(...).expect that such bytes always form a valid TradeId. The invariant is guaranteed by construction (exactly 16 lowercase hex ASCII bytes), so the panic indicates the buffer layout or TradeId validation rules have drifted.
Source
Thrown at crates/adapters/databento/src/decode/market_data.rs:121
fnv1a_mix(&mut hash, &ts_event.to_le_bytes());
fnv1a_mix(&mut hash, &ts_recv.to_le_bytes());
fnv1a_mix(&mut hash, &price.to_le_bytes());
fnv1a_mix(&mut hash, &size.to_le_bytes());
fnv1a_mix(&mut hash, &[side as u8]);
trade_id_from_hash(hash)
}
fn trade_id_from_hash(hash: u64) -> TradeId {
const HEX: &[u8; 16] = b"0123456789abcdef";
let mut bytes = [0u8; 16];
let mut value = hash;
for byte in bytes.iter_mut().rev() {
*byte = HEX[(value & 0x0f) as usize];
value >>= 4;
}
TradeId::from_bytes(&bytes).expect("16 lowercase hex bytes are valid TradeId")
}
#[inline(always)]
#[must_use]
pub(super) fn is_trade_msg(action: c_char) -> bool {
action as u8 as char == 'T'
}
/// Returns `true` if both bid and ask prices are defined (not `i64::MAX`).
///
/// Databento uses `i64::MAX` as a sentinel value for undefined/null prices.
/// A valid quote requires both sides to be defined.
#[inline(always)]
#[must_use]
fn has_valid_bid_ask(bid_px: i64, ask_px: i64) -> bool {
bid_px != i64::MAX && ask_px != i64::MAX
}
View on GitHub (pinned to 18893faf8b)
Solutions
- Verify the byte buffer is exactly 16 bytes and filled with lowercase hex from the HEX table; fix the writer if not.
- Keep TradeId::from_bytes accepting 16 lowercase hex bytes; if validation changed, update the derivation to the new format.
- Report a bug if this fires on an unmodified build — it signals an internal invariant break in the decode path.
Example fix
// before
let bytes = [0u8; 15]; // wrong size -> from_bytes fails -> panic
// after
let mut bytes = [0u8; 16]; // 16 lowercase hex chars exactly
for byte in bytes.iter_mut().rev() { *byte = HEX[(value & 0x0f) as usize]; value >>= 4; } Defensive patterns
Strategy: validation
Validate before calling
// Users cannot trigger this directly; integrators modifying the derivation should assert: assert_eq!(bytes.len(), 16); assert!(bytes.iter().all(|b| b.is_ascii_hexdigit() && !b.is_ascii_uppercase()));
Prevention
- Keep the derivation buffer exactly 16 lowercase hex bytes.
- Run decode tests (CMBP trade messages) whenever TradeId validation changes.
- Treat this panic as a bug report trigger, not a user-facing error.
When it happens
Trigger: Unreachable in normal use; would fire if derive_cmbp_trade_id / the hex writer produced fewer/invalid bytes, or if TradeId::from_bytes validation rules changed to reject 16 hex characters.
Common situations: Code changes that alter the byte buffer size or hex alphabet, or a TradeId definition change, would surface here as a panic when decoding CMBP trade messages.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- API key is valid UTF-8
- Close command should not be drained
- Flush command should not be drained
- Order invariant violated: first event must be OrderInitializ
- DBN message type is not currently supported
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/f668c124d888cbb4.
Report an issue: GitHub.