nautechsystems/nautilus_trader · error

failed to subscribe to user portfolio: {e}

Error message

failed to subscribe to user portfolio: {e}

What it means

Raised in `DeribitExecutionClient::connect` when the private-channel subscription for user portfolio (`subscribe_user_portfolio`) fails, as the last of the three execution subscriptions. Connect aborts since portfolio/margin updates are needed for account-state tracking. On the subsequent subscription-confirmation timeout the client rolls back by unsubscribing all three channels.

Source

Thrown at crates/adapters/deribit/src/execution.rs:571

                .authenticate_session(DERIBIT_EXECUTION_SESSION_NAME)
                .await
                .map_err(|e| anyhow::anyhow!("failed to authenticate WebSocket session: {e}"))?;

            log::debug!("WebSocket client authenticated for execution");

            // Subscribe to user order and trade updates for all instruments
            self.ws_client
                .subscribe_user_orders()
                .await
                .map_err(|e| anyhow::anyhow!("failed to subscribe to user orders: {e}"))?;
            self.ws_client
                .subscribe_user_trades()
                .await
                .map_err(|e| anyhow::anyhow!("failed to subscribe to user trades: {e}"))?;
            self.ws_client
                .subscribe_user_portfolio()
                .await
                .map_err(|e| anyhow::anyhow!("failed to subscribe to user portfolio: {e}"))?;

            if let Err(e) = self.ws_client.wait_for_subscriptions_confirmed(30.0).await {
                // Roll back subscription state so a retry re-sends subscribe requests
                let _ = self.ws_client.unsubscribe_user_orders().await;
                let _ = self.ws_client.unsubscribe_user_trades().await;
                let _ = self.ws_client.unsubscribe_user_portfolio().await;
                anyhow::bail!("subscription confirmation failed: {e}");
            }

            log::debug!("Subscribed to user order, trade, and portfolio updates");

            // Spawn stream handler to dispatch WebSocket messages to the execution engine
            let stream = self.ws_client.stream()?;
            self.spawn_stream_handler(stream)?;

            Ok::<(), anyhow::Error>(())
        }
        .await;

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Read the embedded inner error for the actual cause (transport vs JSON-RPC rejection).
  2. Retry connect; the rollback/unsubscribe logic ensures a clean re-subscription attempt.
  3. Verify the account has permission for portfolio channels and credentials are correctly configured for the target environment.
  4. Check network stability and proxy timeouts around the connect window.
  5. Reduce subscription load or stagger clients if hitting Deribit subscription limits.
Defensive patterns

Strategy: retry

Try / catch

// connect() already rolls back subscriptions on failure; simply retry
match client.connect().await {
    Err(e) if format!("{e:#}").contains("failed to subscribe to user portfolio") => {
        tokio::time::sleep(BACKOFF).await;
        client.connect().await?;
    }
    other => other?,
}

Prevention

When it happens

Trigger: Calling `connect()` where orders and trades subscriptions succeed but `ws_client.subscribe_user_portfolio().await` returns Err — connection failure on the third RPC, rejected `user.portfolio.*` channel subscribe, or session invalidation before this call.

Common situations: Same family as the other subscribe failures: credential scope problems, mid-handshake network drops, channel/quota rejection, environment (testnet/prod) mismatch, or an already-closed ws client.

Understand the failure class

Background: "API request failed": what wrapped HTTP errors from external APIs mean and how to find the real cause — this error's family across 29 libraries.

Related errors


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