nautechsystems/nautilus_trader · critical · anyhow::Error
Execution payload {} changed during rewrap
Error message
Execution payload {} changed during rewrap What it means
During a key-rotation rewrap of a stored execution payload, the payload is unsealed, re-sealed with the new key, unsealed again, and compared byte-for-byte with the original. This error is thrown when the verified plaintext differs from the original raw transaction, meaning the seal/unseal round-trip was not lossless and persisting the rewrapped payload would corrupt it.
Source
Thrown at crates/adapters/blockchain/src/cache/database.rs:5508
anyhow::anyhow!(
"Execution payload {} has no envelope during rewrap",
hash.id
)
})?;
anyhow::ensure!(
hash.raw_transaction.is_none(),
"Execution payload {} contains plaintext during rewrap",
hash.id
);
let intent = load_execution_intent(&mut transaction, hash.intent_id).await?;
let context = payload_context(&intent, &hash, keys.deployment_id())?;
let raw_transaction = keys.unseal(envelope, &context)?;
authenticate_retained_payload(&raw_transaction, &intent, &hash, keys.deployment_id())?;
reserve_execution_payload_seal(&mut transaction, keys.active_key_id()).await?;
let rewrapped = keys.seal(&raw_transaction, &context)?;
let verified = keys.unseal(&rewrapped, &context)?;
authenticate_retained_payload(&verified, &intent, &hash, keys.deployment_id())?;
anyhow::ensure!(
verified == raw_transaction,
"Execution payload {} changed during rewrap",
hash.id
);
let result = sqlx::query(
"UPDATE execution_transaction_hash SET sealed_transaction = $2, updated_at = NOW() \
WHERE id = $1 AND sealed_transaction = $3 AND raw_transaction IS NULL",
)
.bind(hash.id)
.bind(&rewrapped)
.bind(envelope)
.execute(&mut *transaction)
.await
.context("failed to persist rewrapped execution payload")?;
anyhow::ensure!(
result.rows_affected() == 1,
"Execution payload {} changed during rewrap",
hash.idView on GitHub (pinned to 18893faf8b)
Solutions
- Inspect keys.seal/keys.unseal to ensure unseal(seal(x)) == x exactly; fix any re-serialization or normalization inside seal
- Verify the seal context (deployment_id, protocol version) matches the context used for unseal
- Add a round-trip unit test over representative raw transactions to catch encoding drift before rotation runs
- Roll back the transaction (the ensure! aborts before UPDATE) and re-run rotation after fixing the codec
Example fix
// before: seal re-encodes the payload
fn seal(&self, raw: &[u8], ctx: &Context) -> Result<Vec<u8>> {
let tx = Transaction::decode(raw)?; // lossy re-encode
self.cipher.encrypt(&tx.encode(), ctx)
}
// after: seal is byte-preserving
fn seal(&self, raw: &[u8], ctx: &Context) -> Result<Vec<u8>> {
self.cipher.encrypt(raw, ctx)
} Defensive patterns
Strategy: validation
Validate before calling
// Round-trip check before scheduling rotation for a payload let rewrapped = keys.seal(&raw, &ctx)?; assert_eq!(keys.unseal(&rewrapped, &ctx)?, raw, "seal round-trip not byte-preserving");
Prevention
- Keep seal/unseal byte-preserving: never decode-and-re-encode the payload inside seal
- Pin the sealing codec version and test round-trips across upgrades
- Include deployment_id/protocol version in the seal context consistently
When it happens
Trigger: Calling the rewrap routine (within the execution payload key-rotation flow in database.rs) when the cipher or key context used by keys.seal/keys.unseal does not reproduce the exact payload — e.g. a seal implementation that re-encodes the transaction (different serialization, altered metadata) or mismatched deployment_id context between seal and unseal.
Common situations: Upgrading the sealing codec or key-management code so old payloads no longer round-trip identically; swapping serialization libraries or transaction encoding versions; testing with a custom PayloadKeySet whose seal alters the payload; concurrent schema/code versions where the rewrap worker runs newer code than the writer.
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
- Execution payload storage is in {operation} maintenance; com
- Execution payload storage is in {operation} maintenance, not
- Execution payload rewrap left {remaining} row(s)
- Execution payload {} has no envelope during rewrap
- Execution payload {} contains plaintext during rewrap
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/534093c34fe4bfcb.
Report an issue: GitHub.