nautechsystems/nautilus_trader · error · anyhow::Error

Failed to read execution schema version: {e}

Error message

Failed to read execution schema version: {e}

What it means

Thrown when the SELECT of version for component 'evm_execution' from execution_schema_version fails inside ensure_execution_transaction_schema, immediately after the DDL loop. Because that table was just created with IF NOT EXISTS in the same transaction, this almost always indicates a transport-level failure (dropped connection, statement timeout) rather than a missing relation.

Source

Thrown at crates/adapters/blockchain/src/cache/database.rs:3228

                )),
                UNIQUE (intent_id, transition_key)
            )
            ",
        ] {
            sqlx::query(statement)
                .execute(&mut *transaction)
                .await
                .map_err(|e| {
                    anyhow::anyhow!("Failed to migrate execution_transaction table: {e}")
                })?;
        }

        let installed_version = sqlx::query_scalar::<_, i16>(
            "SELECT version FROM execution_schema_version WHERE component = 'evm_execution'",
        )
        .fetch_optional(&mut *transaction)
        .await
        .map_err(|e| anyhow::anyhow!("Failed to read execution schema version: {e}"))?;
        if let Some(installed_version) = installed_version
            && installed_version > EXECUTION_SCHEMA_VERSION
        {
            anyhow::bail!(
                "Execution schema version {} is newer than supported version {EXECUTION_SCHEMA_VERSION}",
                installed_version
            );
        }

        let unresolved_legacy = sqlx::query_scalar::<_, i64>(
            "
            SELECT COUNT(*)
            FROM execution_transaction
            WHERE status IN ('pending', 'included', 'reverted')
            ",
        )
        .fetch_one(&mut *transaction)
        .await

View on GitHub (pinned to 2114cf6f76)

Solutions

  1. Confirm network stability to Postgres and re-run - the whole migration is idempotent (IF NOT EXISTS / ON CONFLICT / OR REPLACE)
  2. Raise statement_timeout for the migration session
  3. If a proxy like PgBouncer sits in the path, run the migration over a direct or session-pooled connection
Defensive patterns

Strategy: retry

Validate before calling

// Health check immediately before the migration to reduce mid-flight transport failures
let healthy = sqlx::query("SELECT 1").execute(&pool).await.is_ok();

Try / catch

for attempt in 0..3 {
    match db.ensure_execution_transaction_schema().await {
        Ok(()) => break,
        Err(e) if e.downcast_ref::<sqlx::Error>().is_some_and(|se| matches!(se, sqlx::Error::Io(_) | sqlx::Error::ConnectionClosed(_))) => {
            tokio::time::sleep(Duration::from_secs(1u64 << attempt)).await;
        }
        Err(e) => return Err(e),
    }
}

Prevention

When it happens

Trigger: Connection reset between the DDL loop and the version read; statement_timeout firing on the migration session; a pooling proxy killing the session mid-transaction.

Common situations: Flaky network path to a managed Postgres; aggressive statement_timeout; failover during migration.

Related errors


AI-assisted analysis of nautechsystems/nautilus_trader@2114cf6f76 (2026-08-21). Data as JSON: /api/errors/2c03433f5844605f. Report an issue: GitHub.