nautechsystems/nautilus_trader · critical · anyhow::Error

Plaintext signed transaction persistence is disabled after p

Error message

Plaintext signed transaction persistence is disabled after payload activation

What it means

After execution payload protection has been activated (an execution_schema_version marker row exists for the component), the database no longer accepts plaintext (raw, unsealed) signed-transaction payloads. This guard prevents a caller from bypassing encryption and writing raw transaction bytes once protected storage is enabled.

Source

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

            let state = execution_payload_state_from_row(&state_row)?;
            anyhow::ensure!(
                state.protocol_version == EXECUTION_PAYLOAD_PROTOCOL_VERSION
                    && state.operation == "ready",
                "Execution payload storage is not ready for protected persistence"
            );
            anyhow::ensure!(
                envelope_key_id(envelope)?.as_slice() == state.active_key_id.as_slice(),
                "Signed transaction envelope does not use the database active key"
            );
        } else {
            let marker = sqlx::query_scalar::<_, bool>(
                "SELECT EXISTS (SELECT 1 FROM execution_schema_version WHERE component = $1)",
            )
            .bind(EXECUTION_PAYLOAD_COMPONENT)
            .fetch_one(&mut *transaction)
            .await
            .context("failed to inspect execution payload marker")?;
            anyhow::ensure!(
                !marker,
                "Plaintext signed transaction persistence is disabled after payload activation"
            );
        }
        let current_status = sqlx::query_scalar::<_, String>(
            "SELECT status FROM execution_intent WHERE id = $1 FOR UPDATE",
        )
        .bind(intent_id)
        .fetch_optional(&mut *transaction)
        .await
        .map_err(|e| anyhow::anyhow!("Failed to lock execution intent {intent_id}: {e}"))?
        .ok_or_else(|| anyhow::anyhow!("Execution intent {intent_id} was not found"))?;
        anyhow::ensure!(
            current_status == TransactionStatus::Prepared.as_str()
                || current_status == TransactionStatus::Signed.as_str(),
            "Execution intent {intent_id} is {current_status}, not prepared for signing"
        );

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Upgrade/restore the sealing path so the payload is passed as sealed_transaction instead of raw_transaction.
  2. If plaintext mode is genuinely required, revert the payload activation (remove the execution_schema_version marker) via the supported migration procedure — only in a maintenance window.
  3. Ensure all writer instances run the same code version with payload protection enabled.
  4. Check deployment configuration so the sealing flag matches the database's activated state.

Example fix

// before
add_execution_transaction_payload(intent, chain, hash, Some(raw_bytes), None).await?;
// after
let sealed = seal(payload, active_key)?;
add_execution_transaction_payload(intent, chain, hash, None, Some(sealed)).await?;
Defensive patterns

Strategy: validation

Validate before calling

let activated: bool = sqlx::query_scalar("SELECT EXISTS (SELECT 1 FROM execution_schema_version WHERE component=$1)").bind(component).fetch_one(&pool).await?;
anyhow::ensure!(!(activated && sealed_transaction.is_none()), "payload protection active: must pass sealed_transaction");

Try / catch

match result {
    Err(e) if e.to_string().contains("Plaintext signed transaction persistence is disabled") => {
        error!("writer lacks payload sealing; upgrade node or enable sealing config before retrying");
    }
    r => r?,
}

Prevention

When it happens

Trigger: Calling add_execution_transaction_payload with raw_transaction=Some (sealed_transaction=None) on a database where the execution_schema_version marker for EXECUTION_PAYLOAD_COMPONENT already exists — i.e. after payload-activation/encryption was enabled.

Common situations: Running an older node version that still writes raw payloads against a database migrated to protected storage; a rollback of code after activation; a config flag turning off sealing while the DB marker remains enabled.

Related errors


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