nautechsystems/nautilus_trader · error

Authentication failed: {e}; handler shutdown failed: {shutdo

Error message

Authentication failed: {e}; handler shutdown failed: {shutdown_error}

What it means

This is the compound variant of the OKX WebSocket authentication failure: auth failed AND the subsequent handler-shutdown/teardown also failed, so both errors are reported together. It surfaces the original auth error plus the shutdown error to avoid hiding either failure during rollback.

Source

Thrown at crates/adapters/okx/src/websocket/client.rs:894

            control.register(move || reconnect_handle.request_reconnect());
        }
        log::debug!("Sent WebSocket client to handler");

        if self.credential.is_some()
            && let Err(e) = self.authenticate().await
        {
            self.handler_tasks.begin_shutdown();
            self.request_close().await;
            let shutdown_result = self.close_stream_task(Duration::from_secs(2)).await;

            if let Some(control) = &self.socket_control {
                control.deregister();
            }
            self.out_rx = None;

            match shutdown_result {
                Ok(()) => anyhow::bail!("Authentication failed: {e}"),
                Err(shutdown_error) => anyhow::bail!(
                    "Authentication failed: {e}; handler shutdown failed: {shutdown_error}"
                ),
            }
        }

        rollback.disarm();
        Ok(())
    }

    /// Authenticates the WebSocket session with OKX.
    async fn authenticate(&self) -> Result<(), Error> {
        let credential = self.credential.as_ref().ok_or_else(|| {
            Error::Io(std::io::Error::other(
                "API credentials not available to authenticate",
            ))
        })?;

        let rx = self.auth_tracker.begin();

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Fix the underlying credential problem first (see the auth failure: key/secret/passphrase, clock sync, permissions).
  2. Investigate the appended `shutdown_error` — usually a handler task that already exited or a shutdown timeout.
  3. After a failed rollback, recreate the client instance rather than reusing it, since internal state (socket control, out_rx) was reset.
  4. Ensure connect/disconnect calls are serialized (await each fully) to avoid racing handler teardown.

Example fix

// before: reusing a client whose teardown half-failed
client.connect().await?; // Authentication failed ... shutdown failed
// after: rebuild after auth-fixing config change
let client = OKXWebSocketClient::new(key, secret, passphrase, ...).await?;
client.connect().await?;
Defensive patterns

Strategy: try-catch

Try / catch

match client.connect().await {
    Err(e) if e.to_string().starts_with("Authentication failed") && e.to_string().contains("shutdown failed") => {
        log::error!("auth + rollback both failed; rebuild the client: {e}");
        client = build_new_client()?;
    }
    other => other?,
}

Prevention

When it happens

Trigger: Same as the plain auth failure ([1038]) — failed OKX login during `connect()` with credentials — combined with the cleanup path (`begin_shutdown`/`close_stream_task`) returning an error, e.g. because the handler task was already dead or timed out.

Common situations: Invalid credentials plus a racing handler-task crash; shutdown timeout (2s) exceeded while the socket is wedged; calling connect during runtime teardown where both auth and cleanup fail.

Understand the failure class

Related errors


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