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.

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.

Appendix: 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)