nautechsystems/nautilus_trader · error · anyhow::Error
Execution payload {} contains plaintext during rewrap
Error message
Execution payload {} contains plaintext during rewrap What it means
During rewrap, a row must hold its payload exclusively in sealed form: `raw_transaction` (plaintext) must be NULL. This `anyhow::ensure!` fires when a row selected for rewrap still carries a plaintext `raw_transaction`, which would mean rewrapping the envelope could desynchronize the two copies or leak the plaintext the encryption scheme is meant to remove. The library refuses to rewrap such rows.
Source
Thrown at crates/adapters/blockchain/src/cache/database.rs:5495
)
.execute(&mut *transaction)
.await
.context("failed to mark execution payload rewrap complete")?;
transaction
.commit()
.await
.context("failed to commit execution payload rewrap completion")?;
return Ok(true);
}
for hash in rows {
let envelope = hash.sealed_transaction.as_deref().ok_or_else(|| {
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(View on GitHub (pinned to 18893faf8b)
Solutions
- Find the offending row by id (from the message) and confirm both `raw_transaction` and `sealed_transaction` are populated.
- Verify the sealed envelope decrypts to the same plaintext (`raw_transaction`), then clear `raw_transaction` (set it NULL) so the rewrap can proceed.
- Identify and stop the code path writing plaintext into `raw_transaction` (old version, fallback writer, ETL) before re-running the rewrap.
- If the plaintext row cannot be trusted, re-create the payload from the transaction source and seal it under the active key, then clear the plaintext column.
Example fix
-- before: plaintext retained alongside sealed envelope SELECT id, raw_transaction, sealed_transaction FROM execution_transaction_hash WHERE id = <id>; -- after: verify envelope matches, then clear plaintext so rewrap can proceed UPDATE execution_transaction_hash SET raw_transaction = NULL, updated_at = NOW() WHERE id = <id>;
Defensive patterns
Strategy: validation
Validate before calling
let plaintext_rows: Vec<i64> = sqlx::query_scalar("SELECT id FROM execution_transaction_hash WHERE payload_expected AND raw_transaction IS NOT NULL")
.fetch_all(&pool).await?;
if !plaintext_rows.is_empty() {
// verify sealed envelopes decrypt to the plaintext, then clear raw_transaction before rewrap
} Try / catch
match rewrap_result {
Err(e) if e.to_string().contains("contains plaintext during rewrap") => {
// parse row id, verify envelope vs plaintext, clear raw_transaction, retry rewrap
},
other => other?,
} Prevention
- Deprecate/remove any code path that writes `raw_transaction` alongside sealed envelopes.
- After a failed rewrap, check whether the plaintext-clearing step ran; re-run remediation before retrying.
- Add a periodic audit query for payload_expected rows with non-NULL raw_transaction and alert immediately (plaintext retention risk).
- Validate restored/pre-encryption-era rows and seal-or-strip plaintext before admitting them into the table.
When it happens
Trigger: A row in the rewrap batch has both `payload_expected=true` and a non-NULL `raw_transaction` — e.g. a writer stored the plaintext transaction after the rewrap began (post-lock race), a prior migration step that clears `raw_transaction` after sealing was interrupted, or manual data manipulation left plaintext in place.
Common situations: An older adapter version or fallback path that wrote plaintext `raw_transaction` while `sealed_transaction` was also populated; a partially completed earlier rewrap where the plaintext-clearing UPDATE never ran; manual imports/backfills that set `raw_transaction` directly; restoring rows from a pre-encryption-era backup.
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 rewrap left {remaining} row(s)
- Execution payload {} has no envelope during rewrap
- Execution payload storage is in {operation} maintenance; com
- Execution payload protection is active, but no payload key i
- Execution payload storage is in {operation} maintenance, not
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/9946be7b1d4af734.
Report an issue: GitHub.