nautechsystems/nautilus_trader · critical · anyhow::Error

Legacy payload storage contains protected state

Error message

Legacy payload storage contains protected state

What it means

When no protection version row exists (legacy storage), the initializer checks that no protected payload state (sealed transaction references) is present before starting fresh. If legacy tables already contain protected state, initializing would lose or orphan it, so it aborts.

Source

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

            .context("failed to stabilize execution payload check")?;
        let marker = sqlx::query_scalar::<_, i16>(
            "SELECT version FROM execution_schema_version WHERE component = $1",
        )
        .bind(EXECUTION_PAYLOAD_COMPONENT)
        .fetch_optional(&mut *transaction)
        .await
        .context("failed to read execution payload marker")?;
        let deployment_id = match (marker, keys) {
            (None, _) => {
                let state = sqlx::query_scalar::<_, bool>(
                    "SELECT EXISTS (SELECT 1 FROM execution_payload_state) \
                     OR EXISTS (SELECT 1 FROM execution_transaction_hash \
                                WHERE sealed_transaction IS NOT NULL)",
                )
                .fetch_one(&mut *transaction)
                .await
                .context("failed to check legacy payload state")?;
                anyhow::ensure!(!state, "Legacy payload storage contains protected state");
                None
            }
            (Some(version), Some(keys)) => {
                anyhow::ensure!(
                    version == EXECUTION_PAYLOAD_PROTOCOL_VERSION,
                    "Unsupported execution payload protection version {version}"
                );
                let state_row = sqlx::query(
                    "SELECT deployment_id, protocol_version, operation, active_key_id \
                     FROM execution_payload_state WHERE component = 'signed_transactions' FOR SHARE",
                )
                .fetch_optional(&mut *transaction)
                .await
                .context("failed to lock execution payload state")?
                .ok_or_else(|| anyhow::anyhow!("Execution payload state is missing"))?;
                let state = execution_payload_state_from_row(&state_row)?;
                validate_execution_payload_state(&state, keys)?;
                anyhow::ensure!(

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Re-run migration to re-establish the version row instead of initializing as legacy
  2. Purge or migrate the protected state before legacy init
  3. Never downgrade node versions against a shared database; restore a pre-protected backup instead
  4. Audit how the version row disappeared
Defensive patterns

Strategy: validation

Validate before calling

let protected: bool = sqlx::query_scalar(
    "SELECT EXISTS (SELECT 1 FROM execution_transaction_hash WHERE sealed_transaction IS NOT NULL)"
).fetch_one(&mut conn).await?;
let versioned: Option<i32> = sqlx::query_scalar("SELECT version FROM execution_payload_version").fetch_optional(&mut conn).await?;
if versioned.is_none() && protected { return Err(anyhow::anyhow!("run migration, not legacy init")); }

Prevention

When it happens

Trigger: Initializing execution payload protection on a database where execution_transaction_hash rows already have sealed_transaction values but no version marker exists.

Common situations: A previous versioned deployment was rolled back/downgraded, deleting the version row but leaving data; mixing old and new node versions against one database.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


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