nautechsystems/nautilus_trader · error · anyhow::Error

Failed to start execution intent reservation: {e}

Error message

Failed to start execution intent reservation: {e}

What it means

Thrown when reserve_execution_intent fails at pool.begin(), before the intent INSERT runs. As with other begin failures, the wrapped sqlx error means a connection could not be acquired: database unreachable, pool exhausted by concurrent activity, or a dropped connection. No signer slot or client order is touched when this fires.

Source

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

    ///
    /// # Errors
    ///
    /// Returns an error if the signer or client order is already owned, or persistence fails.
    pub async fn reserve_execution_intent(
        &self,
        intent: &ExecutionIntentInsert,
    ) -> anyhow::Result<ExecutionIntentRow> {
        let chain_id = i32::try_from(intent.chain_id)
            .with_context(|| format!("Chain ID {} exceeds PostgreSQL INTEGER", intent.chain_id))?;
        let created_block = i64::try_from(intent.created_block).with_context(|| {
            format!(
                "Execution creation block {} exceeds PostgreSQL BIGINT",
                intent.created_block
            )
        })?;
        let mut transaction =
            self.pool.begin().await.map_err(|e| {
                anyhow::anyhow!("Failed to start execution intent reservation: {e}")
            })?;
        let row = sqlx::query_as::<_, ExecutionIntentRow>(
            "
            INSERT INTO execution_intent (
                schema_version, chain_id, wallet_address, purpose, status,
                client_order_id, trader_id, strategy_id, account_id, instrument_id,
                pool_address, transaction_to, transaction_input, transaction_value,
                amount_in, created_block
            )
            VALUES ($1, $2, $3, $4, 'prepared', $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15)
            RETURNING
                id, schema_version, chain_id, wallet_address, nonce, purpose, status,
                client_order_id, trader_id, strategy_id, account_id, instrument_id,
                pool_address, transaction_to, transaction_input, transaction_value,
                amount_in, created_block, acknowledgement_emitted, fill_emitted,
                terminal_emitted, active
            ",
        )

View on GitHub (pinned to 2114cf6f76)

Solutions

  1. Verify database health (SELECT 1) and network connectivity
  2. Increase pool max_connections and acquire_timeout to cover peak concurrency
  3. Bound concurrent reservations with a semaphore so begin() never starves
  4. Retry only after transient connection failures - a successful reserve is exactly-once ownership, not an idempotent write
Defensive patterns

Strategy: retry

Validate before calling

// Cheap liveness probe before attempting a reservation under load
let ok = sqlx::query("SELECT 1").execute(&pool).await.is_ok();

Try / catch

match db.reserve_execution_intent(&intent).await {
    Err(e) if e.downcast_ref::<sqlx::Error>() == Some(&sqlx::Error::PoolTimedOut) => {
        // shed load or expand the pool, then retry; nothing was reserved
    }
    other => other?,
}

Prevention

When it happens

Trigger: Reserving intents while the pool is fully checked out by receipt recorders; database restart mid-trading; acquire_timeout shorter than load spikes allow.

Common situations: Trade bursts exhaust pool capacity; a connection leak elsewhere; failover of the database tier.

Related errors


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