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.operation

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Deploy a binary whose protocol version matches the stored marker (usually the newest release)
  2. Query the stored version to confirm: SELECT version FROM execution_schema_version WHERE component = '<payload component>'
  3. If mixed versions must run, isolate them per database — one protocol version per database
  4. 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

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


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