nautechsystems/nautilus_trader · error

Client never started

Error message

Client never started

What it means

`close` requires the client to have been started (`is_running == true`). Calling close on a client that was constructed but never started is an invalid state transition, so it bails with this error rather than performing a no-op teardown.

Source

Thrown at crates/adapters/databento/src/live.rs:297

            self.bars_timestamp_on_close,
            self.reconnect_timeout_mins,
        );

        self.send_command(HandlerCommand::Start)?;
        self.is_running = true;

        Ok((feed_handler, msg_rx))
    }

    /// Closes the live client.
    ///
    /// # Errors
    ///
    /// Returns an error if the client was never started, is already closed, or cannot send
    /// the close command to the feed handler.
    pub fn close(&mut self) -> anyhow::Result<()> {
        if !self.is_running {
            anyhow::bail!("Client never started");
        }

        if self.is_closed {
            anyhow::bail!("Client already closed");
        }

        log::debug!("Closing client");

        if !self.cmd_tx.is_closed() {
            self.send_command(HandlerCommand::Close)?;
        }

        self.is_running = false;
        self.is_closed = true;

        Ok(())
    }

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Only call close after a successful `start`; track client state before cleanup.
  2. Make cleanup idempotent: skip close when the client was never started or is not running.
  3. If start failed, drop the client instead of closing it.
  4. Guard cleanup loops with a started/running check per client.

Example fix

// before
let mut client = DatabentoLive::new(...)?;
client.close()?; // error: never started
// after
let mut client = DatabentoLive::new(...)?;
client.start().await?;
// ... later ...
client.close()?;
Defensive patterns

Strategy: try-catch

Validate before calling

// Only close clients that were started
if client.is_running() {
    client.close()?;
}

Try / catch

// Tolerate never-started in bulk cleanup
for client in &mut clients {
    if client.is_running() {
        if let Err(e) = client.close() {
            log::warn!("close failed: {e}");
        }
    }
}

Prevention

When it happens

Trigger: Calling `close` (or `py_close`) on a DatabentoLive client before `start` was ever called, or after a failed start left `is_running` false.

Common situations: Error-path cleanup code that unconditionally closes all clients, including ones that never started; shutdown handlers iterating a registry of clients regardless of state; calling close after a start that itself errored.

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/e045a1f3b2356a0d. Report an issue: GitHub.