nautechsystems/nautilus_trader · error · anyhow::Error
Execution transaction {} contains both plaintext and sealed
Error message
Execution transaction {} contains both plaintext and sealed payloads What it means
While migrating plaintext rows to sealed envelopes, each fetched row must be plaintext-only. A row carrying both raw_transaction and sealed_transaction is ambiguous (it would be double-sealed or hand-modified), so migration aborts citing the offending transaction hash id to protect data integrity.
Source
Thrown at crates/adapters/blockchain/src/cache/database.rs:4950
",
)
.bind(batch_size)
.fetch_all(&mut *transaction)
.await
.context("failed to load legacy signed transaction batch")?;
if rows.is_empty() {
self.complete_execution_payload_migration(&mut transaction, keys)
.await?;
transaction
.commit()
.await
.context("failed to commit execution payload migration completion")?;
return Ok(true);
}
for hash in rows {
anyhow::ensure!(
hash.sealed_transaction.is_none(),
"Execution transaction {} contains both plaintext and sealed payloads",
hash.id
);
let raw_transaction = hash
.raw_transaction
.as_deref()
.expect("migration query requires plaintext");
let intent = load_execution_intent(&mut transaction, hash.intent_id).await?;
let context = authenticate_retained_payload(
raw_transaction,
&intent,
&hash,
keys.deployment_id(),
)
.with_context(|| format!("failed to authenticate execution payload {}", hash.id))?;
reserve_execution_payload_seal(&mut transaction, keys.active_key_id()).await?;
let envelope = keys.seal(raw_transaction, &context)?;View on GitHub (pinned to 18893faf8b)
Solutions
- Re-run the batch — the fenced transaction should re-select only consistent rows
- If the row genuinely holds both payloads, repair it via the library (re-seal from plaintext, clearing the stale sealed value) or restore from backup
- Ensure only one migration/sealing process runs at a time against the table
- Audit for manual writes to execution_transaction_hash and restrict direct table access
Example fix
-- find inconsistent rows SELECT id FROM execution_transaction_hash WHERE raw_transaction IS NOT NULL AND sealed_transaction IS NOT NULL; -- repair: clear stale sealed value (only if plaintext is authoritative) then retry migration UPDATE execution_transaction_hash SET sealed_transaction = NULL WHERE id = <offending_id>;
Defensive patterns
Strategy: retry
Validate before calling
let bad: i64 = sqlx::query_scalar(
"SELECT COUNT(*) FROM execution_transaction_hash
WHERE raw_transaction IS NOT NULL AND sealed_transaction IS NOT NULL",
).fetch_one(&pool).await?;
if bad > 0 { anyhow::bail!("{bad} rows hold both plaintext and sealed payloads; repair before migrating"); } Try / catch
match db.migrate_execution_payload_batch(&keys, 500).await {
Ok(done) => { /* continue batches */ }
Err(e) if e.to_string().contains("both plaintext and sealed") => {
// extract hash id from message, repair the row, resume migration
}
Err(e) => return Err(e),
} Prevention
- Seal rows exclusively through the library's fenced migration/seal path
- Disable direct writes to execution_transaction_hash in production
- Run one migration worker at a time
- After any failed migration, audit for rows with both payloads populated
When it happens
Trigger: During migrate_execution_payload_batch, an execution_transaction_hash row is returned with sealed_transaction set even though the query selected pending (plaintext) rows — the row changed concurrently or was written inconsistently.
Common situations: Another process sealed the row between selection and processing without proper fencing; manual UPDATEs on execution_transaction_hash; a previous failed migration wrote sealed values without clearing plaintext.
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
- Migrated terminal transaction hash was not found
- Unprotected execution payload storage contains {sealed_rows}
- Execution payload migration found {invalid} invalid represen
- Execution payload rewrap left {remaining} row(s)
- Execution payload {} has no envelope during rewrap
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/56ea68165ae43483.
Report an issue: GitHub.