nautechsystems/nautilus_trader · error

rollback query requires envelope

Error message

rollback query requires envelope

What it means

During the rollback query path, a transaction-hash row contains both a raw plaintext and a sealed representation, and the code dereferences `sealed_transaction` expecting the sealed envelope — panicking with "rollback query requires envelope" if it is `None`. The rollback path decrypts from the sealed envelope, so a missing envelope is treated as an invariant violation.

Source

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

                    .context("failed to complete execution payload rollback")?;
            }
            transaction
                .commit()
                .await
                .context("failed to commit execution payload rollback completion")?;
            return Ok(true);
        }

        for hash in rows {
            anyhow::ensure!(
                hash.raw_transaction.is_none(),
                "Execution payload {} contains both representations during rollback",
                hash.id
            );
            let envelope = hash
                .sealed_transaction
                .as_deref()
                .expect("rollback query requires envelope");
            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())?;
            let result = sqlx::query(
                "UPDATE execution_transaction_hash SET raw_transaction = $2, updated_at = NOW() \
                 WHERE id = $1 AND raw_transaction IS NULL AND sealed_transaction = $3",
            )
            .bind(hash.id)
            .bind(&raw_transaction)
            .bind(envelope)
            .execute(&mut *transaction)
            .await
            .context("failed to recreate plaintext execution payload")?;
            anyhow::ensure!(
                result.rows_affected() == 1,
                "Execution payload {} changed during rollback",
                hash.id

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Seal the plaintext payload (or re-run the migration that produces envelopes) before rolling back.
  2. Filter plaintext-only rows and handle them without unsealing in the rollback path.
  3. Backfill `sealed_transaction` for affected rows.
  4. Ensure the rollback code path matches the storage representation of the rows being processed.

Example fix

// before
let envelope = hash.sealed_transaction.as_deref()
    .expect("rollback query requires envelope");
// after
let envelope = match hash.sealed_transaction.as_deref() {
    Some(env) => env,
    None => {
        let raw = hash.raw_transaction.as_deref()
            .ok_or_else(|| anyhow!("row {} has neither payload form", hash.id))?;
        // proceed using the plaintext form directly
        raw
    }
};
Defensive patterns

Strategy: validation

Validate before calling

if hash.sealed_transaction.is_none() {
    // handle as plaintext-only row, or seal it before rollback
}

Type guard

fn has_envelope(h: &ExecutionTransactionHashRow) -> bool { h.sealed_transaction.is_some() }

Try / catch

let Some(envelope) = hash.sealed_transaction.as_deref() else {
    return Err(anyhow!("row {} stored plaintext-only; seal before rollback", hash.id));
};

Prevention

When it happens

Trigger: Calling the rollback query on an `execution_transaction_hash` row whose `sealed_transaction` is NULL (the row was stored plaintext-only), after detecting both representations present in the row logic.

Common situations: Rolling back a database where rows were written plaintext-only by an older code path; partial migration leaving rows without sealed envelopes; schema/data written by a version that never sealed payloads.

Related errors


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