nautechsystems/nautilus_trader · error · anyhow::Error

Payload rollback requires an active payload key

Error message

Payload rollback requires an active payload key

What it means

rollback_payload_storage() decrypts authenticated plaintext payloads and removes protection, requiring the active payload key set to authenticate the stored ciphertext. When load_payload_keys() returns None the rollback cannot proceed and the client refuses the operation rather than leaving the storage in a half-protected state.

Source

Thrown at crates/adapters/blockchain/src/execution/client.rs:919

            .rewrap_execution_payload_storage(&keys, batch_size)
            .await
    }

    /// Restores authenticated plaintext payloads and removes protection from this database.
    ///
    /// This incident-only operation is resumable. Keep the complete key set configured until it
    /// succeeds and the unprotected database passes a full payload check.
    ///
    /// # Errors
    ///
    /// Returns an error if the client is connected, storage is not protected, required keys are
    /// unavailable, or any bounded rollback batch fails authentication.
    pub async fn rollback_payload_storage(&self, batch_size: usize) -> anyhow::Result<()> {
        let batch_size = validate_payload_operation_batch_size(batch_size)?;
        let database = self.payload_operation_database().await?;
        let keys = self
            .load_payload_keys()?
            .ok_or_else(|| anyhow::anyhow!("Payload rollback requires an active payload key"))?;
        database
            .rollback_execution_payload_storage(&keys, batch_size)
            .await
    }

    async fn payload_operation_database(&self) -> anyhow::Result<BlockchainCacheDatabase> {
        anyhow::ensure!(
            !self.core.is_connected(),
            "Disconnect the execution client before payload storage operations"
        );

        if let Some(database) = &self.cache.database {
            return Ok(database.clone());
        }
        let options = self
            .config
            .postgres_cache_database_config
            .as_ref()

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Initialize keys first with protect_payload_storage(), or load them via the normal connect flow, then rollback.
  2. Use check_payload_storage() to confirm keys exist and protection state before rolling back.
  3. Fix key store configuration so load_payload_keys() resolves the persisted keys.
  4. If the storage was never protected, no rollback is needed — skip the call.

Example fix

// before
client.rollback_payload_storage(500).await?;
// after
anyhow::ensure!(
    client.check_payload_storage().await?.has_keys,
    "initialize keys before rollback"
);
client.rollback_payload_storage(500).await?;
Defensive patterns

Strategy: validation

Validate before calling

let status = client.check_payload_storage().await?;
anyhow::ensure!(status.has_keys, "payload storage has no keys; nothing to rollback");
client.rollback_payload_storage(batch_size).await?;

Prevention

When it happens

Trigger: Calling client.rollback_payload_storage(batch_size) when no payload keys were loaded (never protected, keys cleared, or key store unavailable).

Common situations: Attempting rollback on a database that was never protected; running rollback from a client instance whose connect sequence did not initialize keys; key store misconfiguration after redeploy.

Understand the failure class

Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.

Related errors


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