nautechsystems/nautilus_trader · error · anyhow::Error

Execution payload has no envelope during rewrap

Error message

Execution payload {} has no envelope during rewrap

What it means

While rewrapping, each row selected for rewrap must have a sealed envelope in `sealed_transaction` to unseal and re-seal under the new key. This error fires when a row in the batch has `payload_expected=true` but `sealed_transaction IS NULL`, so there is no envelope to rewrap. The library treats this as unrecoverable data corruption rather than silently skipping the payload.

Solutions

  1. Locate the corrupt row by id (included in the message) and inspect its `sealed_transaction`, `raw_transaction`, and `payload_expected` values.
  2. Restore the envelope from a backup or source of truth, or re-create the payload: fetch the plaintext transaction and seal it under the active key before re-running the rewrap.
  3. If the row should not have a payload at all, set `payload_expected=false` so it is excluded from rewrap batches.
  4. Fix the writer path that produced payload_expected rows without envelopes, then retry the rewrap.
Defensive patterns

Strategy: validation

Validate before calling

let bad: Vec<i64> = sqlx::query_scalar("SELECT id FROM execution_transaction_hash WHERE payload_expected AND sealed_transaction IS NULL")
    .fetch_all(&pool).await?;
if !bad.is_empty() {
    // remediate (re-seal or set payload_expected=false) before starting a rewrap
}

Try / catch

match rewrap_result {
    Err(e) if e.to_string().contains("has no envelope during rewrap") => {
        // parse row id from message, restore/re-seal or clear payload_expected, then retry
    },
    other => other?,
}

Prevention

When it happens

Trigger: A row matched the batch query but has a NULL `sealed_transaction` — possible when the row is mutated between the batch SELECT and processing, or when data was written by a path that marked `payload_expected` without storing a sealed envelope; also when the verification SELECT (with its OR `sealed_transaction IS NULL` clause) and the batch SELECT disagree on NULL-envelope rows due to concurrent modification.

Common situations: A crashed writer inserted rows with `payload_expected=true` but never populated `sealed_transaction`; a restore/ETL backfill left envelopes NULL; concurrent transactions cleared `sealed_transaction` while the rewrap loop held the batch rows FOR UPDATE but the row was updated in a window not covered by the lock.

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


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

Appendix: source

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

                "Execution payload rewrap left {remaining} row(s)"
            );
            sqlx::query(
                "UPDATE execution_payload_state SET operation = 'ready', updated_at = NOW() \
                 WHERE component = 'signed_transactions' AND operation = 'rewrap'",
            )
            .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!(

View on GitHub (pinned to 18893faf8b)