nautechsystems/nautilus_trader · error

Execution payload protection is active, but no payload key i

Error message

Execution payload protection is active, but no payload key is configured

What it means

inspect_execution_payload_storage validates that when the execution payload protection marker exists in execution_schema_version, a PayloadKeySet must also be supplied so persisted sealed payloads can be authenticated. The (Some(_), None) arm fires when protection is active in the database but the caller passed no keys. Without the payload key there is no way to decrypt or verify stored signed transactions, so the check aborts.

Source

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

                    "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!(
                    state.operation == "ready",
                    "Execution payload storage is not ready"
                );
                Some(state.deployment_id)
            }
            (Some(_), None) => anyhow::bail!(
                "Execution payload protection is active, but no payload key is configured"
            ),
        };

        let mut cursor = 0_i64;
        let mut plaintext_rows = 0_u64;
        let mut original_rows = 0_u64;
        let mut replacement_rows = 0_u64;
        let mut authenticated_rows = 0_u64;
        let mut key_ids = BTreeSet::new();

        loop {
            let rows = sqlx::query_as::<_, ExecutionTransactionHashRow>(
                "
                SELECT
                    id, intent_id, chain_id, transaction_hash, payload_expected,
                    raw_transaction, sealed_transaction, status, block_number, block_hash,
                    receipt_success, gas_used, effective_gas_price, current

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Provide the configured PayloadKeySet (load payload keys via the key provider/passphrase used when protection was activated) to the inspect/require call
  2. If protection is no longer wanted, first run the payload rollback procedure with the correct keys to return storage to ready/unprotected, then inspect without keys
  3. Verify the key source (env var, KMS, config file) is present in this environment; do not open protected databases from shells lacking the key

Example fix

// before
let (check, _tx) = db.inspect_execution_payload_storage(None, Some(policy), batch).await?;
// after
let keys = load_payload_keys()?; // fetch configured payload key set
let (check, _tx) = db.inspect_execution_payload_storage(Some(&keys), Some(policy), batch).await?;
Defensive patterns

Strategy: validation

Validate before calling

let protected: Option<i16> = sqlx::query_scalar(
    "SELECT version FROM execution_schema_version WHERE component = $1")
    .bind("component_id") // EXECUTION_PAYLOAD_COMPONENT
    .fetch_optional(&pool).await?;
anyhow::ensure!(keys.is_some() || protected.is_none(),
    "payload protection active: configure payload keys before inspecting");

Type guard

fn keys_available_for(protected_db: bool, keys: Option<&PayloadKeySet>) -> Option<&PayloadKeySet> {
    if protected_db { keys } else { None.or(keys) }
}

Try / catch

match db.inspect_execution_payload_storage(keys_opt, policy, batch).await {
    Err(e) if e.to_string().contains("no payload key is configured") => {
        // load keys from KMS/env and retry once
    }
    r => r?,
}

Prevention

When it happens

Trigger: Calling inspect_execution_payload_storage (or its wrappers such as require_execution_payload_storage / maintenance checks) with keys = None against a database where the execution payload protection marker row exists — i.e. a previously protected deployment being opened without configured payload keys.

Common situations: Operator omitted the payload key/passphrase from config after enabling encryption; environment variable holding the key not set in the new deployment; connecting a fresh tool or shell session to a protected production database without loading keys; config rollback removed the key while the database kept protection.

Understand the failure class

Background: "API key is required" / "API key not found" / "No API key was set": the missing-api-key error family across 16 libraries — this error's family across 16 libraries.

Related errors


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