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.
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
- book channel present
- bundled America/New_York timezone
- Chunk should have at least one element to encode
- Close command should not be drained
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/f668c124d888cbb4.
Report an issue: GitHub.
Appendix: 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)