nautechsystems/nautilus_trader · critical
handler command channel closed: {e}
Error message
handler command channel closed: {e} What it means
When submitting a Kraken Spot order over WebSocket, the request is dispatched to the handler task through an mpsc command channel. If that channel's receiver has been dropped (the handler task has shut down), the send fails; the adapter removes the pending-request entry it just registered and surfaces the channel error.
Source
Thrown at crates/adapters/kraken/src/websocket/dispatch/spot_orders.rs:268
) -> anyhow::Result<u64> {
let req_id = self.next_req_id();
envelope.req_id = Some(req_id);
identity.ts_sent_ns = ts_now_ns;
let payload = SecretString::from(
serde_json::to_string(&envelope)
.map_err(|e| anyhow::anyhow!("serialize WS order request: {e}"))?,
);
let cmd_tx = self
.cmd_tx()
.ok_or_else(|| anyhow::anyhow!("WS handler command sender unavailable"))?;
self.pending.insert(req_id, identity);
if let Err(e) = cmd_tx.send(SpotHandlerCommand::SendOrderRequest { req_id, payload }) {
self.pending.remove(&req_id);
anyhow::bail!("handler command channel closed: {e}");
}
let state_for_timeout = Arc::downgrade(self);
let task_spawner = self.task_spawner.read().clone();
let cancel = task_spawner.cancellation_token();
let timeout = self.timeout;
if let Err(e) = task_spawner.spawn(async move {
tokio::select! {
biased;
() = cancel.cancelled() => {
if let Some(state) = state_for_timeout.upgrade() {
state.pending.remove(&req_id);
}
}
() = tokio::time::sleep(timeout) => {
let Some(state) = state_for_timeout.upgrade() else {
return;View on GitHub (pinned to 18893faf8b)
Solutions
- Check WS connection state and reconnect before retrying the order submission.
- Implement an on-disconnect callback/hook that pauses order flow until the handler task is live again.
- Retry the order through a freshly (re)connected Kraken Spot WS client.
- Log and alert on this error — it usually indicates the adapter's background task died and needs supervision.
Example fix
// before
client.send_order(payload)?; // may fail after handler died
// after
if !client.is_handler_alive() { client.reconnect().await?; }
client.send_order(payload)?; Defensive patterns
Strategy: try-catch
Validate before calling
if !ws_client.is_connected() || !ws_client.is_handler_alive() {
ws_client.reconnect().await?;
} Try / catch
match dispatch.send(req_id, payload) {
Err(e) if e.to_string().contains("handler command channel closed") => {
pending.remove(&req_id);
reconnect_and_resubmit().await?;
}
r => r?,
} Prevention
- Supervise the WS handler task and auto-reconnect on unexpected exit.
- Pause order submission while disconnected; queue and flush after reconnect.
- Track connection state and check it before every submission.
- Alert on this error: it means the background task died, not just one bad request.
When it happens
Trigger: Calling send on the Kraken Spot order dispatch after the WS handler task has terminated — e.g. the connection was dropped/disconnected and the handler loop exited, but the dispatch handle is still being used to place an order.
Common situations: Ordering during a WebSocket disconnect or reconnect window; network outage killing the handler task while strategy keeps trading; shutting down the adapter while in-flight logic still attempts submissions.
Related errors
- limit_price is required for order type {order_type:?}
- missing WS auth token
- WS submit order failed: {e}
- WS setup error channel closed
- Cannot submit order: execution client not connected
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/da73737aa5ebfbbe.
Report an issue: GitHub.