nautechsystems/nautilus_trader · error

Execution client is not started

Error message

Execution client is not started

What it means

query_account requires the execution client to be in the started state before it reads wallet balances and generates an AccountState. If core.is_started() is false — the client was constructed but never initialized/connected, or was stopped/degraded — the command is rejected. This prevents reporting balances from an uninitialized wallet/signer.

Source

Thrown at crates/adapters/blockchain/src/execution/client.rs:5812

        );
        Ok(())
    }

    fn batch_cancel_orders(&self, cmd: BatchCancelOrders) -> anyhow::Result<()> {
        for cancel in cmd.cancels {
            self.cancel_order(cancel)?;
        }
        Ok(())
    }

    fn query_account(&self, cmd: QueryAccount) -> anyhow::Result<()> {
        anyhow::ensure!(
            cmd.account_id == self.core.account_id,
            "Query account ID {} does not match client account ID {}",
            cmd.account_id,
            self.core.account_id
        );
        anyhow::ensure!(self.core.is_started(), "Execution client is not started");

        let balances = self.wallet_balance.lock().as_account_balances()?;
        self.generate_account_state(
            balances,
            vec![],
            true,
            get_atomic_clock_realtime().get_time_ns(),
            None,
        )
    }

    fn query_order(&self, cmd: QueryOrder) -> anyhow::Result<()> {
        log::warn!(
            "Order queries are not supported on the blockchain execution client; cannot query {}",
            cmd.client_order_id
        );
        Ok(())
    }

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Ensure the client is fully started/connected before issuing QueryAccount — wait for the started/initialized lifecycle event
  2. Call the client's connect/start path (which acquires the signer and wallet balances) if it was never started
  3. Reconnect or restart the client if it was stopped, then re-issue the query
  4. Gate downstream actors so they only query after the execution client reports readiness

Example fix

// before
client.query_account(cmd)?; // may run before start
// after
if client.core.is_started() {
    client.query_account(cmd)?;
} else {
    client.connect().await?;
    client.query_account(cmd)?;
}
Defensive patterns

Strategy: type-guard

Validate before calling

// gate queries on client readiness
if !client.is_started() {
    return Err(anyhow::anyhow!("defer query: execution client not started"));
}

Type guard

fn can_query(client: &ExecutionClient) -> bool {
    client.core.is_started()
}

Try / catch

match client.query_account(cmd) {
    Err(e) if e.to_string().contains("not started") => {
        // wait/retry until lifecycle reports started
        wait_for_started(&client, Duration::from_secs(30)).await?;
        client.query_account(cmd)
    }
    other => other,
}

Prevention

When it happens

Trigger: Calling query_account (or emitting a QueryAccount on the bus) before connect()/initialize has completed, after disconnect/stop, or while the client is in a degraded/reconnecting state.

Common situations: An actor issues a balance query during startup before the execution client finished connecting; the client dropped its connection and stopped; a query is replayed after a restart; lifecycle wiring starts consumers before the client is fully initialized.

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


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