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

  1. Fix the producer so it writes the UUID as exactly 16 raw bytes via uuid.as_bytes() before encoding into the capnp field.
  2. Verify both sides use the same generated capnp schema (cargo build regenerates from the same .capnp files).
  3. Inspect the offending message's raw payload length to find truncation at the transport layer.
  4. 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

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


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