nautechsystems/nautilus_trader · error
Execution payload storage is in {operation} maintenance, not
Error message
Execution payload storage is in {operation} maintenance, not ready for rollback What it means
begin_execution_payload_rollback converts protected payload storage back to its unprotected representation. It accepts state.operation of 'ready' (start a new rollback) or 'rollback' (resume an interrupted one); any other maintenance value (e.g. 'migrate' or 'rewrap') makes rollback unsafe because payloads are in another transitional representation. The library bails so operators cannot interleave conflicting maintenance modes.
Source
Thrown at crates/adapters/blockchain/src/cache/database.rs:5601
"ready" => {
sqlx::query(
"ALTER TABLE execution_transaction_hash \
DROP CONSTRAINT IF EXISTS execution_transaction_payload_protected_check",
)
.execute(&mut *transaction)
.await
.context("failed to open execution payload rollback representation")?;
sqlx::query(
"UPDATE execution_payload_state \
SET operation = 'rollback', progress_id = 0, updated_at = NOW() \
WHERE component = 'signed_transactions'",
)
.execute(&mut *transaction)
.await
.context("failed to record execution payload rollback state")?;
}
"rollback" => {}
operation => anyhow::bail!(
"Execution payload storage is in {operation} maintenance, not ready for rollback"
),
}
transaction
.commit()
.await
.context("failed to commit execution payload rollback state")?;
Ok(())
}
async fn rollback_execution_payload_batch(
&self,
keys: &PayloadKeySet,
batch_size: i64,
) -> anyhow::Result<bool> {
let mut transaction = self
.pool
.begin()View on GitHub (pinned to 18893faf8b)
Solutions
- Finish the current operation first: drain 'migrate' batches to ready or complete the 'rewrap' rotation, then start the rollback
- If the goal is to abort a rotation, use the appropriate rotation tooling to finish/cancel it so state returns to 'ready' before rollback
- Check SELECT operation FROM execution_payload_state to confirm the phase and coordinate with whoever/whatever started it
- Schedule protection changes in a single serialized maintenance window
Example fix
// before: rollback during active rewrap begin_execution_payload_rollback(&keys).await?; // after: let rotation finish, then roll back finish_rewrap_to_ready(&keys).await?; // operation == 'ready' begin_execution_payload_rollback(&keys).await?;
Defensive patterns
Strategy: validation
Validate before calling
let op: String = sqlx::query_scalar(
"SELECT operation FROM execution_payload_state WHERE component = 'signed_transactions'")
.fetch_one(&pool).await?;
anyhow::ensure!(matches!(op.as_str(), "ready" | "rollback"),
"cannot roll back while storage operation is '{op}'"); Type guard
fn rollback_allowed(operation: &str) -> bool {
matches!(operation, "ready" | "rollback")
} Try / catch
match db.begin_execution_payload_rollback(&keys).await {
Err(e) if e.to_string().contains("not ready for rollback") => {
// let migrate/rewrap finish to 'ready', then retry rollback
}
r => r?,
} Prevention
- Wait for migrations/rotations to reach 'ready' before initiating rollback
- Use an advisory lock so rollback cannot interleave with rotation tooling
- Monitor execution_payload_state for non-ready operations older than expected
- Plan protection removal as the final step of a serialized maintenance window
When it happens
Trigger: Calling the rollback command (begin_execution_payload_rollback) while execution_payload_state.operation is 'migrate' or 'rewrap' — for example trying to disable protection while the initial encryption migration is still running, or while an active key rotation is in progress.
Common situations: Operator attempted to roll back protection before the initial migration finished; a key rotation was started by another process and rollback was issued concurrently; resume scripts invoked rollback while rotation tooling still held the state; misread state table led to ordering mistakes.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- Execution payload storage is in {operation} maintenance; com
- Execution payload storage is in {operation} maintenance, not
- Execution payload protection is active, but no payload key i
- Protected execution transaction {} contains plaintext
- Protected execution transaction {} has no envelope
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/3c0f5ecb5559d664.
Report an issue: GitHub.