nautechsystems/nautilus_trader · error · anyhow::Error

Failed to send SubscribeUser: {e}

Error message

Failed to send SubscribeUser: {e}

What it means

subscribe_user() sends HandlerCommand::SubscribeUser to the feed-handler task to authenticate and open the private user channel. The send fails when the handler's receiver was dropped — the handler task has exited, so no session exists for the authenticated subscription. Note the flag user_subscribed is only set after a successful send, precisely to avoid replaying a subscription that never got enqueued.

Source

Thrown at crates/adapters/polymarket/src/websocket/client.rs:570

            .map_err(|e| anyhow::anyhow!("Failed to send UnsubscribeMarket: {e}"))
    }

    /// Authenticate and subscribe to the user channel.
    ///
    /// # Errors
    ///
    /// Returns an error if called on a market-channel client (no credentials available).
    pub async fn subscribe_user(&self) -> anyhow::Result<()> {
        if self.channel != WsChannel::User {
            anyhow::bail!(
                "subscribe_user() requires a user-channel client (created with new_user())"
            );
        }
        self.cmd_tx
            .read()
            .await
            .send(HandlerCommand::SubscribeUser)
            .map_err(|e| anyhow::anyhow!("Failed to send SubscribeUser: {e}"))?;
        // Set only after the command is successfully enqueued so a failed send does not
        // leave user_subscribed=true and cause an unintended replay on the next connect().
        self.user_subscribed.store(true, Ordering::Relaxed);
        Ok(())
    }

    /// Returns a cloneable subscription handle for use in spawned tasks.
    #[must_use]
    pub fn clone_subscription_handle(&self) -> WsSubscriptionHandle {
        WsSubscriptionHandle {
            cmd_tx: Arc::clone(&self.cmd_tx),
        }
    }

    /// Takes the message receiver, leaving `None` in its place.
    ///
    /// This is useful when the data client needs to spawn its own handler
    /// task that reads messages independently of the WS client.

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Verify the client is connected (is_active()) and call connect() first if not
  2. Retry subscribe_user() after reconnecting; the user subscription is replayed on reconnect once user_subscribed=true
  3. Fix the root cause of the handler's exit (check wrapped error in logs, refresh auth credentials)
  4. Serialize lifecycle calls (connect/subscribe/disconnect) to avoid races

Example fix

// before
client.subscribe_user().await?;
// after
if !client.is_active() {
    client.connect().await?;
}
client.subscribe_user().await?;
Defensive patterns

Strategy: try-catch

Validate before calling

if !client.is_active() {
    client.connect().await?;
}
// credentials must be present for the user channel
debug_assert!(!api_key.is_empty(), "user channel requires credentials");

Try / catch

match client.subscribe_user().await {
    Err(e) if e.to_string().contains("Failed to send SubscribeUser") => {
        client.connect().await?;
        client.subscribe_user().await?;
    }
    other => other?,
}

Prevention

When it happens

Trigger: Calling subscribe_user() after disconnect(), after the handler task died (connection loss, panic, auth startup failure), or during a concurrent reconnect.

Common situations: Enabling the private order/fill feed while the gateway is down; credentials problems killed the handler earlier; shutdown racing an in-flight subscribe_user() from another task.

Related errors


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