nautechsystems/nautilus_trader · error

migration query requires plaintext

Error message

migration query requires plaintext

What it means

During the migration query path, a transaction-hash row contains both a plaintext `raw_transaction` and a sealed payload, and the code dereferences the plaintext field expecting it to be present — panicking with "migration query requires plaintext" if it is `None`. The migration path only supports rows stored as plaintext, so a missing raw transaction is treated as a programmer/data invariant violation.

Source

Thrown at crates/adapters/blockchain/src/cache/database.rs:4958

            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)?;
            let unsealed = keys.unseal(&envelope, &context)?;
            authenticate_retained_payload(&unsealed, &intent, &hash, keys.deployment_id())?;
            anyhow::ensure!(
                unsealed == raw_transaction,
                "Execution payload {} changed during seal round trip",
                hash.id
            );
            let result = sqlx::query(

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Unseal the sealed payload into plaintext (with `keys.unseal`) before running the migration path.
  2. Re-run the migration only on rows stored in plaintext; filter sealed-only rows out beforehand.
  3. Restore or backfill `raw_transaction` for the affected rows.
  4. Check that the migration code path matches the storage representation of your rows (plaintext vs sealed).

Example fix

// before
let raw_transaction = hash.raw_transaction.as_deref()
    .expect("migration query requires plaintext");
// after
let raw_transaction = match hash.raw_transaction.as_deref() {
    Some(raw) => raw,
    None => {
        let envelope = hash.sealed_transaction.as_deref()
            .ok_or_else(|| anyhow!("row {} has neither payload form", hash.id))?;
        &keys.unseal(envelope, &payload_context(&intent, &hash, keys.deployment_id())?)?
    }
};
Defensive patterns

Strategy: validation

Validate before calling

if hash.raw_transaction.is_none() {
    // unseal from sealed_transaction first, or skip this row in the migration
}

Type guard

fn has_plaintext(h: &ExecutionTransactionHashRow) -> bool { h.raw_transaction.is_some() }

Try / catch

let Some(raw) = hash.raw_transaction.as_deref() else {
    return Err(anyhow!("row {} stored sealed-only; unseal before migration", hash.id));
};

Prevention

When it happens

Trigger: Calling the migration query on an `execution_transaction_hash` row whose `raw_transaction` is NULL (the row was stored sealed-only), after the code has already flagged the both-representations conflict.

Common situations: Migrating a database where rows were written by a newer sealed-payload code path; mixed old/new schema rows; running the migration against data written by a version that sealed payloads without keeping plaintext.

Related errors


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