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

  1. Verify the byte buffer is exactly 16 bytes and filled with lowercase hex from the HEX table; fix the writer if not.
  2. Keep TradeId::from_bytes accepting 16 lowercase hex bytes; if validation changed, update the derivation to the new format.
  3. 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

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


AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08). Data as JSON: /api/errors/f668c124d888cbb4. Report an issue: GitHub.