nautechsystems/nautilus_trader · error
WS handler command sender unavailable
Error message
WS handler command sender unavailable
What it means
The Kraken spot orders dispatcher has no command sender (`cmd_tx`) available, so it cannot forward the serialized order request to the WebSocket handler task. `cmd_tx()` returns None when the WS handler command channel has not been established (e.g. not connected) or has been dropped. The order is aborted before being inserted into `pending`.
Source
Thrown at crates/adapters/kraken/src/websocket/dispatch/spot_orders.rs:262
fn send(
self: &Arc<Self>,
mut envelope: KrakenWsRequest,
mut identity: PendingRequest,
ts_now_ns: u64,
) -> 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() {View on GitHub (pinned to 18893faf8b)
Solutions
- Ensure the Kraken WebSocket client/handler is started and connected before submitting orders.
- Check adapter logs for prior disconnect/shutdown events and re-establish the WS connection.
- Add connection-state checks or retry logic so orders are only sent when the WS handler is live.
- If the handler task panicked or exited, inspect its logs and restart the adapter/client.
Defensive patterns
Strategy: validation
Validate before calling
// Only send when the WS handler is connected
if !ws_client.is_connected() { log::warn!("Kraken WS not connected; skipping order send"); return; } Type guard
fn has_cmd_tx(dispatcher: &SpotOrdersDispatcher) -> bool {
dispatcher.cmd_tx().is_some()
} Try / catch
match dispatcher.send(order) {
Ok(()) => {},
Err(e) if e.to_string().contains("command sender unavailable") => reconnect_and_resubmit(),
Err(e) => log::error!("order send failed: {e}"),
} Prevention
- Gate order submission on WS connection state.
- Monitor adapter disconnect events and pause trading until reconnected.
- Ensure the WS handler task is started before the execution engine.
When it happens
Trigger: Calling `send` on the spot orders dispatcher before the WebSocket connection/handler is started, or after the handler task has shut down and dropped the receiving end of the command channel.
Common situations: Submitting an order during adapter startup before the WS client is fully connected; orders submitted after a disconnect or shutdown of the WS handler; lifecycle races where the account/executor is live while the WS handler is not.
Understand the failure class
Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.
Related errors
- Failed to send Subscribe command: {e}
- Failed to send Unsubscribe command: {e}
- std::mem::take(&mut self.shutdown_errors).join("; ")
- Binance Spot public JSON stream pool is shutting down
- No active WebSocket client
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/86031f5a63441ea0.
Report an issue: GitHub.