{"record":{"id":"f668c124d888cbb4","repo":"nautechsystems/nautilus_trader","slug":"16-lowercase-hex-bytes-are-valid-tradeid","errorCode":null,"errorMessage":"16 lowercase hex bytes are valid TradeId","messagePattern":"16 lowercase hex bytes are valid TradeId","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"crates/adapters/databento/src/decode/market_data.rs","lineNumber":121,"sourceCode":"    fnv1a_mix(&mut hash, &ts_event.to_le_bytes());\n    fnv1a_mix(&mut hash, &ts_recv.to_le_bytes());\n    fnv1a_mix(&mut hash, &price.to_le_bytes());\n    fnv1a_mix(&mut hash, &size.to_le_bytes());\n    fnv1a_mix(&mut hash, &[side as u8]);\n    trade_id_from_hash(hash)\n}\n\nfn trade_id_from_hash(hash: u64) -> TradeId {\n    const HEX: &[u8; 16] = b\"0123456789abcdef\";\n\n    let mut bytes = [0u8; 16];\n    let mut value = hash;\n    for byte in bytes.iter_mut().rev() {\n        *byte = HEX[(value & 0x0f) as usize];\n        value >>= 4;\n    }\n\n    TradeId::from_bytes(&bytes).expect(\"16 lowercase hex bytes are valid TradeId\")\n}\n\n#[inline(always)]\n#[must_use]\npub(super) fn is_trade_msg(action: c_char) -> bool {\n    action as u8 as char == 'T'\n}\n\n/// Returns `true` if both bid and ask prices are defined (not `i64::MAX`).\n///\n/// Databento uses `i64::MAX` as a sentinel value for undefined/null prices.\n/// A valid quote requires both sides to be defined.\n#[inline(always)]\n#[must_use]\nfn has_valid_bid_ask(bid_px: i64, ask_px: i64) -> bool {\n    bid_px != i64::MAX && ask_px != i64::MAX\n}\n","sourceCodeStart":103,"sourceCodeEnd":139,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/databento/src/decode/market_data.rs#L103-L139","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nlet bytes = [0u8; 15]; // wrong size -> from_bytes fails -> panic\n// after\nlet mut bytes = [0u8; 16]; // 16 lowercase hex chars exactly\nfor byte in bytes.iter_mut().rev() { *byte = HEX[(value & 0x0f) as usize]; value >>= 4; }","handlingStrategy":"validation","validationCode":"// Users cannot trigger this directly; integrators modifying the derivation should assert:\nassert_eq!(bytes.len(), 16);\nassert!(bytes.iter().all(|b| b.is_ascii_hexdigit() && !b.is_ascii_uppercase()));","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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."],"tags":["rust","panic","databento","invariant","decoding"],"backgroundTag":"internal-invariant-violation","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}