nautechsystems/nautilus_trader · critical · anyhow::Error
Legacy payload storage contains protected state
Error message
Legacy payload storage contains protected state
What it means
When no protection version row exists (legacy storage), the initializer checks that no protected payload state (sealed transaction references) is present before starting fresh. If legacy tables already contain protected state, initializing would lose or orphan it, so it aborts.
Source
Thrown at crates/adapters/blockchain/src/cache/database.rs:5149
.context("failed to stabilize execution payload check")?;
let marker = sqlx::query_scalar::<_, i16>(
"SELECT version FROM execution_schema_version WHERE component = $1",
)
.bind(EXECUTION_PAYLOAD_COMPONENT)
.fetch_optional(&mut *transaction)
.await
.context("failed to read execution payload marker")?;
let deployment_id = match (marker, keys) {
(None, _) => {
let state = sqlx::query_scalar::<_, bool>(
"SELECT EXISTS (SELECT 1 FROM execution_payload_state) \
OR EXISTS (SELECT 1 FROM execution_transaction_hash \
WHERE sealed_transaction IS NOT NULL)",
)
.fetch_one(&mut *transaction)
.await
.context("failed to check legacy payload state")?;
anyhow::ensure!(!state, "Legacy payload storage contains protected state");
None
}
(Some(version), Some(keys)) => {
anyhow::ensure!(
version == EXECUTION_PAYLOAD_PROTOCOL_VERSION,
"Unsupported execution payload protection version {version}"
);
let state_row = sqlx::query(
"SELECT deployment_id, protocol_version, operation, active_key_id \
FROM execution_payload_state WHERE component = 'signed_transactions' FOR SHARE",
)
.fetch_optional(&mut *transaction)
.await
.context("failed to lock execution payload state")?
.ok_or_else(|| anyhow::anyhow!("Execution payload state is missing"))?;
let state = execution_payload_state_from_row(&state_row)?;
validate_execution_payload_state(&state, keys)?;
anyhow::ensure!(View on GitHub (pinned to 18893faf8b)
Solutions
- Re-run migration to re-establish the version row instead of initializing as legacy
- Purge or migrate the protected state before legacy init
- Never downgrade node versions against a shared database; restore a pre-protected backup instead
- Audit how the version row disappeared
Defensive patterns
Strategy: validation
Validate before calling
let protected: bool = sqlx::query_scalar(
"SELECT EXISTS (SELECT 1 FROM execution_transaction_hash WHERE sealed_transaction IS NOT NULL)"
).fetch_one(&mut conn).await?;
let versioned: Option<i32> = sqlx::query_scalar("SELECT version FROM execution_payload_version").fetch_optional(&mut conn).await?;
if versioned.is_none() && protected { return Err(anyhow::anyhow!("run migration, not legacy init")); } Prevention
- Never downgrade node versions against a shared database
- Run the migration path instead of legacy init when protected data exists
- Track version rows in deployment tooling
- Back up before any version transition
When it happens
Trigger: Initializing execution payload protection on a database where execution_transaction_hash rows already have sealed_transaction values but no version marker exists.
Common situations: A previous versioned deployment was rolled back/downgraded, deleting the version row but leaving data; mixing old and new node versions against one database.
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
- Failed to add pool event block hash storage: {e}
- Failed to start execution schema migration: {e}
- Failed to lock legacy execution transactions: {e}
- Failed to migrate execution_transaction table: {e}
- Failed to read execution schema version: {e}
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/b99523da859e5d10.
Report an issue: GitHub.