nautechsystems/nautilus_trader · error · anyhow::Error

WS setup error channel closed

Error message

WS setup error channel closed

What it means

Raised inside `wait_for_ws_setup_response` when the setup-error channel closes without delivering an error message (`recv()` returned None). It indicates the WS setup supervision tasks shut down before either signalling success or an error — an unexpected lifecycle state during connection establishment.

Source

Thrown at crates/adapters/binance/src/spot/execution.rs:2201

) {
    for report in reports {
        normalize_spot_order_status_report(report, treat_expired_as_canceled);
    }
}

async fn wait_for_ws_setup_response(
    timeout: Duration,
    success: impl Future<Output = ()>,
    setup_errors: &mut tokio::sync::mpsc::UnboundedReceiver<String>,
    timeout_message: &'static str,
) -> anyhow::Result<()> {
    tokio::pin!(success);

    let result = tokio::time::timeout(timeout, async {
        tokio::select! {
            () = &mut success => Ok(()),
            err = setup_errors.recv() => {
                anyhow::bail!(
                    "{}",
                    err.unwrap_or_else(|| "WS setup error channel closed".to_string()),
                )
            }
        }
    })
    .await;

    result.map_err(|_| anyhow::anyhow!(timeout_message))?
}

#[expect(clippy::too_many_arguments)]
fn dispatch_ws_trading_message(
    msg: BinanceSpotWsTradingMessage,
    emitter: &ExecutionEventEmitter,
    http_client: &BinanceSpotHttpClient,
    account_id: AccountId,
    treat_expired_as_canceled: bool,

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Retry the connect — the channel closure is usually a race with shutdown rather than a protocol error
  2. Check whether connect was invoked concurrently with disconnect/teardown and serialize the calls
  3. Enable debug logging around WS setup tasks to see why the sender dropped
  4. If reproducible, inspect the setup task lifecycle for early termination (task abort/panic)
Defensive patterns

Strategy: retry

Try / catch

match connect().await {
    Err(e) if e.to_string().contains("WS setup error channel closed") => {
        tokio::time::sleep(backoff).await;
        // retry connect once
    }
    Err(e) => log::error!("connect failed: {e}"),
    Ok(()) => {},
}

Prevention

When it happens

Trigger: During `connect`, while waiting (with timeout) for the WS API session setup (session logon / subscription) to succeed, the `setup_errors` mpsc channel is dropped by its sender before an error message is sent — e.g. the setup task was aborted or completed without sending.

Common situations: Connect racing with a shutdown; a bug or early task termination in the WS setup pipeline; process shutting down while connect is still in flight.

Understand the failure class

Background: ECONNREFUSED and "connection refused" / "could not connect to server" errors: what they mean and how to fix them — this error's family across 44 libraries.

Related errors


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