nautechsystems/nautilus_trader · critical · anyhow::Error
Unsupported execution payload protection version {version}
Error message
Unsupported execution payload protection version {version} What it means
In `acquire_execution_payload_lease`, once the protection marker is found, its version must equal EXECUTION_PAYLOAD_PROTOCOL_VERSION. A differing stored version means the database's payload protection protocol does not match this binary's protocol, so the lease is refused to prevent incompatible reads/writes of protected signed transactions.
Source
Thrown at crates/adapters/blockchain/src/cache/database.rs:4700
keys: &PayloadKeySet,
) -> anyhow::Result<ExecutionPayloadLease> {
let mut transaction = self
.pool
.begin()
.await
.context("failed to start execution payload action lease")?;
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 version = marker.ok_or_else(|| {
anyhow::anyhow!("Postgres execution requires protected payload storage")
})?;
anyhow::ensure!(
version == EXECUTION_PAYLOAD_PROTOCOL_VERSION,
"Unsupported execution payload protection version {version}"
);
let 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 marker exists without state"))?;
let state = execution_payload_state_from_row(&row)?;
validate_execution_payload_state(&state, keys)?;
anyhow::ensure!(
state.operation == "ready",
"Execution payload storage is in {} maintenance",
state.operationView on GitHub (pinned to 18893faf8b)
Solutions
- Deploy a binary whose protocol version matches the stored marker (usually the newest release)
- Query the stored version to confirm: SELECT version FROM execution_schema_version WHERE component = '<payload component>'
- If mixed versions must run, isolate them per database — one protocol version per database
- Never hand-edit the stored version to force a match; that bypasses data-compatibility protection
Example fix
// before: old node leasing against migrated DB
let lease = db.acquire_execution_payload_lease(&keys).await?; // error: unsupported version
// after: match deployment to DB protocol version
let v: i16 = sqlx::query_scalar(
"SELECT version FROM execution_schema_version WHERE component = $1")
.bind(EXECUTION_PAYLOAD_COMPONENT).fetch_one(&pool).await?;
if v != EXECUTION_PAYLOAD_PROTOCOL_VERSION { return Err(update_binary); } Defensive patterns
Strategy: validation
Validate before calling
let v: Option<i16> = sqlx::query_scalar(
"SELECT version FROM execution_schema_version WHERE component = $1")
.bind(EXECUTION_PAYLOAD_COMPONENT).fetch_optional(&pool).await?;
match v {
None => return Err(anyhow::anyhow!("storage not activated")),
Some(v) if v != EXECUTION_PAYLOAD_PROTOCOL_VERSION => {
return Err(anyhow::anyhow!("binary/DB protocol mismatch: DB={} binary={}; deploy matching node version", v, EXECUTION_PAYLOAD_PROTOCOL_VERSION));
}
_ => {}
} Type guard
fn protocol_matches(stored: i16, supported: i16) -> bool { stored == supported } Try / catch
match db.acquire_execution_payload_lease(&keys).await {
Err(e) if e.to_string().contains("Unsupported execution payload protection version") => {
eprintln!("halting: deploy the node release matching the database protocol version");
return Err(e);
}
other => other?,
} Prevention
- Rolling upgrades: update all nodes before/with the database protocol migration
- Read the stored version in deployment pre-flight and abort mismatches
- Never hand-edit the stored version to bypass the check
- Keep one database per protocol version during migrations
When it happens
Trigger: Calling `acquire_execution_payload_lease` (any signing/broadcast action) when the stored version differs — typically a database upgraded by a newer release (or theoretically an older one) while this process runs a binary compiled against a different EXECUTION_PAYLOAD_PROTOCOL_VERSION.
Common situations: Version skew between rolling-updated nodes and a database already migrated by newer code; accidentally launching an old container image against a migrated database; separate deployments sharing one database with different protocol versions.
Understand the failure class
Background: "is not a compatible type" / "cannot merge" errors: when a value's type doesn't match what the library requires — this error's family across 65 libraries.
Related errors
- Execution payload protection version {version} is newer than
- Wrong schema ID: expected {crate::spot::sbe::spot::SBE_SCHEM
- unsupported subscribed RTDS message topic={topic} type={msg_
- Invalid stream message format: {stream_msg:?}
- Invalid stream field key: {stream_msg:?}
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/4256b2fa3cd3bf93.
Report an issue: GitHub.