nautechsystems/nautilus_trader · error · std::io::Error
Invalid UUID4 bytes length
Error message
Invalid UUID4 bytes length
What it means
Cap'n Proto decoding of a UUID4 expects the message field to carry exactly 16 raw bytes. The reader's value is converted via try_into() into a [u8; 16]; any other byte length fails conversion, and this InvalidData Io error is returned inside Box<dyn Error> from from_capnp, meaning the incoming payload's UUID field is corrupt or written by an incompatible schema/version.
Source
Thrown at crates/serialization/src/capnp/conversions.rs:159
}
Ok(map)
}
impl<'a> ToCapnp<'a> for nautilus_core::UUID4 {
type Builder = base_capnp::u_u_i_d4::Builder<'a>;
fn to_capnp(&self, mut builder: Self::Builder) {
builder.set_value(&self.as_bytes());
}
}
impl<'a> FromCapnp<'a> for nautilus_core::UUID4 {
type Reader = base_capnp::u_u_i_d4::Reader<'a>;
fn from_capnp(reader: Self::Reader) -> Result<Self, Box<dyn Error>> {
let bytes = reader.get_value()?;
let bytes_array: [u8; 16] = bytes.try_into().map_err(|_| {
std::io::Error::new(
std::io::ErrorKind::InvalidData,
"Invalid UUID4 bytes length",
)
})?;
let uuid = Uuid::from_bytes(bytes_array);
Ok(Self::from(uuid))
}
}
// Decimal
// rust_decimal serialization format (16 bytes):
// - Bytes 0-3: flags (u32) - scale and sign
// - Bytes 4-7: lo (u32) - low 32 bits of coefficient
// - Bytes 8-11: mid (u32) - middle 32 bits of coefficient
// - Bytes 12-15: hi (u32) - high 32 bits of coefficient
fn decimal_to_parts(value: &Decimal) -> (u64, u64, u64, u32) {
let bytes = value.serialize();
let flags = u32::from_le_bytes(bytes[0..4].try_into().expect("flags slice"));View on GitHub (pinned to 18893faf8b)
Solutions
- Fix the producer so it writes the UUID as exactly 16 raw bytes via uuid.as_bytes() before encoding into the capnp field.
- Verify both sides use the same generated capnp schema (cargo build regenerates from the same .capnp files).
- Inspect the offending message's raw payload length to find truncation at the transport layer.
- Reject/replicate or drop malformed messages at the receive loop so one bad frame does not break the stream.
Example fix
// before (producer) builder.set_value(uuid.to_string().as_bytes()); // after (producer) builder.set_value(uuid.as_bytes()); // &[u8; 16]
Defensive patterns
Strategy: try-catch
Validate before calling
if reader.get_value()?.len() != 16 {
return Err("capnp UUID4 field must be exactly 16 bytes".into());
} Type guard
fn is_uuid4_bytes(b: &[u8]) -> bool { b.len() == 16 } Try / catch
match UUID4::from_capnp(reader) {
Ok(uuid) => uuid,
Err(e) => { log::error!("bad UUID4 field: {e}"); continue; } // skip malformed frame
} Prevention
- Always serialize UUIDs with uuid.as_bytes() (16 bytes), never as strings.
- Keep both peers on the same generated capnp schema version.
- Frame-check message lengths at the transport layer to catch truncation early.
When it happens
Trigger: Calling UUID4::from_capnp on a base_capnp::u_u_i_d4::Reader whose value buffer is not 16 bytes long — e.g. a peer serializing a UUID string, a truncated message, or a schema version writing a different byte-width field.
Common situations: Deserializing messages produced by a different nautilus version or another implementation with a mismatched capnp schema; corrupted or truncated transport frames; manually constructed capnp messages with hex-string UUIDs instead of 16 raw bytes.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- Invalid NodeState value
- Cap'n Proto decoding requires the `capnp` feature
- from_json not implemented for {}
- Failed to deserialize record: {e}
- failed to decode Cap'n Proto {}: {}
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/819598b85a17c8a0.
Report an issue: GitHub.