nautechsystems/nautilus_trader · error · anyhow::Error

Stored execution payload requires unavailable key {}

Error message

Stored execution payload requires unavailable key {}

What it means

After deriving the key ID from each stored envelope header, the code requires that key to be present in the configured set of available payload keys. A stored payload encrypted with a key the operator has not provisioned cannot be decrypted, so the validation refuses to continue and names the missing key ID.

Source

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

) -> anyhow::Result<()> {
    let envelopes = sqlx::query_scalar::<_, Vec<u8>>(
        "SELECT DISTINCT substring(sealed_transaction FROM 1 FOR 33) \
         FROM execution_transaction_hash \
         WHERE sealed_transaction IS NOT NULL",
    )
    .fetch_all(&mut **transaction)
    .await
    .context("failed to inspect execution payload key inventory")?;

    for header in envelopes {
        anyhow::ensure!(
            header.len() == 33,
            "Stored execution payload has a truncated envelope header"
        );
        let mut envelope = header;
        envelope.extend_from_slice(&[0; 12 + 16]);
        let key_id = envelope_key_id(&envelope)?;
        anyhow::ensure!(
            keys.contains_key(&key_id),
            "Stored execution payload requires unavailable key {}",
            alloy::hex::encode(key_id)
        );
    }
    Ok(())
}

async fn load_execution_intent(
    transaction: &mut Transaction<'_, Postgres>,
    intent_id: i64,
) -> anyhow::Result<ExecutionIntentRow> {
    sqlx::query_as::<_, ExecutionIntentRow>(
        "
        SELECT
            id, schema_version, chain_id, wallet_address, nonce, purpose, status,
            client_order_id, trader_id, strategy_id, account_id, instrument_id,
            pool_address, transaction_to, transaction_input, transaction_value,

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Provision the missing key (hex key ID shown in the message) into the configured key set
  2. Re-encrypt or purge payloads sealed with retired keys as part of the rotation procedure
  3. Keep old keys available (archived) until all payloads they sealed are expired or re-sealed
Defensive patterns

Strategy: validation

Validate before calling

let required: Vec<[u8;32]> = derive_required_key_ids(&stored_envelopes)?;
let missing: Vec<_> = required.iter().filter(|k| !keys.contains_key(k)).collect();
if !missing.is_empty() { provision_keys(missing)?; }

Prevention

When it happens

Trigger: Validating stored execution payloads when at least one envelope's key ID is absent from the provided key map — e.g. after key rotation removed an old key still referenced by stored data.

Common situations: Retiring/rotating keys without retaining old keys needed to read historical payloads; provisioning a new node with only the current key while the DB holds older envelopes; divergent key sets between replicas.

Related errors


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