nautechsystems/nautilus_trader · error

Failed to start Lighter task generation: {e}

Error message

Failed to start Lighter task generation: {e}

What it means

During Lighter connection setup, after finishing any prior session shutdown, the client starts task generation on pending_tasks (a task group). If start_generation() returns an error, it is wrapped in this anyhow error and connection setup aborts. This means the client's internal task scheduler could not begin producing background tasks for the new connection session.

Source

Thrown at crates/adapters/lighter/src/execution.rs:4066

            );
        }

        log::info!(
            "Connecting Lighter execution client {}",
            self.core.client_id
        );

        // Synchronous stop/reset can only initiate teardown. Complete it before
        // publishing a replacement socket or sharing its connection epoch.
        if !self.session_tasks_finished() || !self.pending_tasks.is_open() {
            self.begin_session_shutdown();
            self.finish_session_shutdown().await?;
        }

        if !self.pending_tasks.is_open() {
            self.pending_tasks
                .start_generation()
                .map_err(|e| anyhow::anyhow!("Failed to start Lighter task generation: {e}"))?;
            self.cancellation_token = self.pending_tasks.cancellation_token();
        }

        let ws_client = self.ws_client.clone();
        let setup_guard = TaskGroupGuard::new(&[&self.pending_tasks], move || {
            ws_client.begin_shutdown();
        });
        self.auth_refresh_notify = Arc::new(tokio::sync::Notify::new());

        // Reset the readiness gate and clear derived position/account caches
        // so a prior session's state cannot leak past the strict-await gate.
        // The Reconnected path (WS-layer transparent reconnect) is unaffected:
        // it does not re-enter `connect()`. Its next `account_all_positions`
        // frame replaces the position cache through the consumption loop.
        self.dispatch.account_streams_ready.reset();
        self.dispatch.clear_position_cache();
        self.dispatch.clear_account_state_cache();

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Ensure the previous connection's shutdown fully completed before reconnecting (await disconnect/shutdown before connect)
  2. Avoid concurrent connect() calls on the same client instance; serialize connections
  3. If the group is permanently wedged, rebuild/recreate the execution client instead of reusing it

Example fix

// before
self.client.connect().await?;
// after
self.client.disconnect().await?;
self.client.connect().await?;
Defensive patterns

Strategy: try-catch

Validate before calling

if client.is_connected() || client.is_shutting_down() {
    return Err(anyhow!("client busy"));
}

Try / catch

match client.connect().await {
    Err(e) if e.to_string().contains("task generation") => {
        // recreate or retry after full shutdown
    }
    other => other?,
}

Prevention

When it happens

Trigger: Calling connect/connect_async when the task group's generation counter cannot advance — e.g. the group was already shut down and not reset, or its internal state (token/generation) is in a conflicting state from a previous failed shutdown.

Common situations: Rapid reconnect loops where a previous session shutdown raced the new start; cancellation token leaked from a cancelled prior session; calling connect twice concurrently on the same client.

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