nautechsystems/nautilus_trader · error
Sealed transaction payload is truncated
Error message
Sealed transaction payload is truncated
What it means
parse_envelope requires the envelope to contain at least the fixed header (version byte + key id + nonce) plus the AES-GCM authentication tag (ENVELOPE_HEADER_LEN + TAG_LEN). If the byte slice is shorter, it cannot contain a complete sealed envelope and is rejected before any field slicing occurs.
Source
Thrown at crates/adapters/blockchain/src/execution/sealing.rs:408
pub(crate) fn envelope_key_id(envelope: &[u8]) -> anyhow::Result<[u8; KEY_ID_LEN]> {
Ok(parse_envelope(envelope)?.key_id)
}
struct ParsedEnvelope<'a> {
key_id: [u8; KEY_ID_LEN],
nonce: &'a [u8],
ciphertext_and_tag: &'a [u8],
}
fn parse_envelope(envelope: &[u8]) -> anyhow::Result<ParsedEnvelope<'_>> {
anyhow::ensure!(
envelope.len() <= MAX_SEALED_TRANSACTION_BYTES,
"Sealed transaction payload is {} bytes, exceeding the {} byte limit",
envelope.len(),
MAX_SEALED_TRANSACTION_BYTES
);
anyhow::ensure!(
envelope.len() >= ENVELOPE_HEADER_LEN + TAG_LEN,
"Sealed transaction payload is truncated"
);
anyhow::ensure!(
envelope[0] == ENVELOPE_VERSION,
"Unsupported signed transaction payload envelope version {}",
envelope[0]
);
let key_id = envelope[1..1 + KEY_ID_LEN]
.try_into()
.expect("fixed key ID slice length");
let nonce_start = 1 + KEY_ID_LEN;
let ciphertext_start = nonce_start + NONCE_LEN;
Ok(ParsedEnvelope {
key_id,
nonce: &envelope[nonce_start..ciphertext_start],
ciphertext_and_tag: &envelope[ciphertext_start..],View on GitHub (pinned to 18893faf8b)
Solutions
- Check envelope.len() >= ENVELOPE_HEADER_LEN + TAG_LEN before calling unseal and log the actual length
- Re-fetch or re-seal the transaction; the stored payload is irrecoverably incomplete
- Fix the persistence layer that truncated the bytes (column size, blob write, serialization)
Example fix
// before
unseal(&stored_bytes, ...)?;
// after
if stored_bytes.len() < ENVELOPE_HEADER_LEN + TAG_LEN {
return Err(anyhow::anyhow!("stored sealed tx is {} bytes, minimum is {}", stored_bytes.len(), ENVELOPE_HEADER_LEN + TAG_LEN));
}
unseal(&stored_bytes, ...)?; Defensive patterns
Strategy: validation
Validate before calling
fn has_minimum_envelope_len(envelope: &[u8]) -> anyhow::Result<()> {
anyhow::ensure!(
envelope.len() >= ENVELOPE_HEADER_LEN + TAG_LEN,
"envelope too short: {} bytes",
envelope.len()
);
Ok(())
} Type guard
fn is_complete_envelope(envelope: &[u8]) -> bool {
envelope.len() >= ENVELOPE_HEADER_LEN + TAG_LEN
} Try / catch
match unseal(&blob, deployment_id) {
Ok(tx) => tx,
Err(e) if e.to_string().contains("truncated") => {
// treat payload as irrecoverable; re-seal from source of truth
}
Err(e) => return Err(e),
} Prevention
- Use a length-prefixed or checksummed storage format for sealed blobs
- Verify blob length after every read from the database
- Avoid fixed-size DB columns for variable-length sealed payloads
When it happens
Trigger: Calling unseal() or envelope_key_id() with a byte slice shorter than ENVELOPE_HEADER_LEN + TAG_LEN — e.g. a DB BLOB that was truncated, a partially written file, or slicing off the last 16 tag bytes.
Common situations: Database column truncation (e.g. storing bytes in a fixed-size or length-limited column); failed/interrupted write during persist; a caller passes the plaintext transaction instead of the sealed envelope.
Understand the failure class
Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.
Related errors
- Collect event data is too short
- CollectProtocol event data is too short
- Sealed transaction payload is {} bytes, exceeding the {} byt
- Unsupported signed transaction payload envelope version {}
- Payload deployment ID does not match the configured key set
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/6c5c14d8c7246e1a.
Report an issue: GitHub.