nautechsystems/nautilus_trader · error · anyhow::Error
Execution transaction {} requires an unavailable payload key
Error message
Execution transaction {} requires an unavailable payload key What it means
The row carries a sealed envelope whose header names a payload key id, but that key id is not present in the PayloadKeySet supplied to the loader. Without the key the envelope cannot be unsealed, so the ensure! fails fast instead of silently dropping the transaction.
Source
Thrown at crates/adapters/blockchain/src/cache/database.rs:5237
}
original_rows += 1;
let intent = load_execution_intent(&mut transaction, hash.intent_id).await?;
let raw_transaction = if let (Some(keys), Some(deployment_id)) =
(keys, deployment_id.as_deref())
{
anyhow::ensure!(
hash.raw_transaction.is_none(),
"Protected execution transaction {} contains plaintext",
hash.id
);
let envelope = hash.sealed_transaction.as_deref().ok_or_else(|| {
anyhow::anyhow!(
"Protected execution transaction {} has no envelope",
hash.id
)
})?;
let key_id = envelope_key_id(envelope)?;
anyhow::ensure!(
keys.contains_key(&key_id),
"Execution transaction {} requires an unavailable payload key",
hash.id
);
key_ids.insert(alloy::hex::encode(key_id));
let context = payload_context(&intent, hash, deployment_id)?;
keys.unseal(envelope, &context)?
} else {
anyhow::ensure!(
hash.sealed_transaction.is_none(),
"Legacy execution transaction {} contains an envelope",
hash.id
);
hash.raw_transaction.clone().ok_or_else(|| {
anyhow::anyhow!("Legacy execution transaction {} has no plaintext", hash.id)
})?
};
authenticate_retained_payload(View on GitHub (pinned to 18893faf8b)
Solutions
- Add the missing payload key (id from the error's key id) to the PayloadKeySet before loading.
- Import the retired wrapping key from your secret manager / backup so old envelopes remain decryptable.
- Re-run rewrap_execution_payload_storage with a key set that includes both old and new active keys so envelopes are rewrapped to the current key.
- Verify key configuration matches execution_payload_state.active_key_id and deployment_id.
Example fix
// before: key set missing retired key
let keys = PayloadKeySet::new(active_key);
// after: include retained old keys during rotation window
let keys = PayloadKeySet::builder()
.active_key(new_key)
.retained_key(old_key) // key id referenced by existing envelopes
.build(); Defensive patterns
Strategy: fallback
Validate before calling
// Ensure all envelope key ids are covered before loading
for key_id in collect_envelope_key_ids(&rows)? {
if !keys.contains_key(&key_id) {
return Err(anyhow!("payload key {} missing from key set", alloy::hex::encode(key_id)));
}
} Prevention
- Keep retired wrapping keys in the key set for at least one full rewrap cycle.
- Back up wrapping keys in a secret manager before rotation.
- Log envelope key ids and diff them against configured key ids at startup.
When it happens
Trigger: Loading protected execution transactions where envelope_key_id(envelope) returns a key id absent from the provided keys map — e.g. the key was rotated out of the config, or a stale/old wrapping key was never imported.
Common situations: Key rotation completed on the database side but the node's key configuration still lacks the retired key; a fresh environment missing legacy keys from a secret store; copying a database between deployments with different key inventories.
Related errors
- Execution payload storage is in {operation} maintenance; com
- Execution payload protection is active, but no payload key i
- 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/b71a9add3952d378.
Report an issue: GitHub.