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, currentView on GitHub (pinned to 18893faf8b)
Solutions
- Provide the configured PayloadKeySet (load payload keys via the key provider/passphrase used when protection was activated) to the inspect/require call
- 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
- 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
- Keep the payload key provider configured wherever the database is accessed, including ops shells and tooling
- Fail fast at config load when the DB has protection enabled but no key is set
- Do not remove key config while the protection marker exists; roll back protection first
- Document key sourcing (env var/KMS) in runbooks for protected deployments
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
- Stored execution payload requires unavailable key {}
- Execution payload storage is in {operation} maintenance; com
- Execution payload storage is in {operation} maintenance, not
- Execution payload storage is in {operation} maintenance, not
- Protected execution transaction {} contains plaintext
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/1037ceb1f9838f36.
Report an issue: GitHub.