nautechsystems/nautilus_trader · error

failed Derive private WS subscriptions

Error message

failed Derive private WS subscriptions

What it means

This error is returned by Derive execution client connect() when subscribing to the private WebSocket channels (orders, balances) fails but the partial-connect teardown succeeded cleanly. The original subscription error is the source; the context confirms startup was rolled back and no partial connection remains. It surfaces from connect().

Source

Thrown at crates/adapters/derive/src/execution.rs:651

            }
            return Err(e);
        };

        let subaccount_id = self.credential.subaccount_id();
        let channels = vec![
            DeriveWsChannel::orders(subaccount_id),
            DeriveWsChannel::private_trades(subaccount_id),
            DeriveWsChannel::balances(subaccount_id),
        ];

        if let Err(e) = self.ws_client.subscribe_channels(channels).await {
            log::warn!("Derive private WS subscriptions failed: {e}; tearing down");
            if let Err(teardown_error) = self.teardown_partial_connect().await {
                return Err(anyhow::Error::new(e).context(format!(
                    "Derive execution startup teardown failed: {teardown_error}"
                )));
            }
            return Err(anyhow::Error::new(e).context("failed Derive private WS subscriptions"));
        }

        if let Err(e) = self.start_ws_dispatch(rx) {
            if let Err(teardown_error) = self.teardown_partial_connect().await {
                return Err(e.context(format!(
                    "Derive execution startup teardown failed: {teardown_error}"
                )));
            }
            return Err(e.context("failed to register Derive execution WebSocket dispatch task"));
        }

        // Fail-fast if the initial account snapshot cannot load: without it,
        // `await_account_registered` would block the full timeout window and
        // surface a misleading registration timeout. Tear down the WS we
        // already started so the caller does not leak the dispatch task.
        if let Err(e) = self.refresh_account_state().await {
            log::warn!("Initial Derive account state refresh failed: {e}; tearing down");
            if let Err(teardown_error) = self.teardown_partial_connect().await {

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Verify Derive API credentials, wallet, and subaccount_id configuration
  2. Read the chained source error to see the specific subscription rejection
  3. Retry connect() — transient network/service issues resolve on retry
  4. Check Derive API status for outages
  5. Ensure the WS client version/protocol matches the current Derive API
Defensive patterns

Strategy: retry

Validate before calling

// Validate Derive credentials and subaccount before attempting subscription
if !derive_credentials_valid(config) {
    return Err(anyhow!("invalid Derive credentials; refusing connect"));
}

Type guard

fn is_clean_subscription_failure(err: &anyhow::Error) -> bool {
    err.to_string().contains("failed Derive private WS subscriptions")
}

Try / catch

match exec_client.connect().await {
    Err(e) if is_clean_subscription_failure(&e) => {
        // rollback succeeded; safe to retry with backoff
        retry_with_backoff(|| exec_client.connect()).await
    }
    other => other,
}

Prevention

When it happens

Trigger: ws_client.subscribe_channels(channels) returns Err and teardown_partial_connect() completes successfully.

Common situations: Invalid or expired Derive API credentials rejecting subscriptions; Derive rejecting a channel due to wrong subaccount; transient network failure at startup; Derive service outage.

Related errors


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