nautechsystems/nautilus_trader · error

wss_rpc_url is required

Error message

wss_rpc_url is required

What it means

BlockchainDataClientCore::new reads `config.wss_rpc_url` when live WebSocket data is enabled (not using HyperSync) and unwraps it with `expect("wss_rpc_url is required")`. The guard `config.wss_rpc_url.is_some()` makes the panic nearly unreachable, but it documents that a WebSocket URL is mandatory on this path — a None here is a config/invariant violation.

Source

Thrown at crates/adapters/blockchain/src/data/core.rs:187

    #[must_use]
    pub fn new(
        config: BlockchainDataClientConfig,
        hypersync_tx: Option<tokio::sync::mpsc::UnboundedSender<BlockchainMessage>>,
        data_tx: Option<tokio::sync::mpsc::UnboundedSender<DataEvent>>,
        cancellation_token: tokio_util::sync::CancellationToken,
    ) -> Self {
        let chain = config.chain.clone();
        let cache = BlockchainCache::new(chain.clone());

        // Log RPC endpoints being used
        log::debug!(
            "Initializing blockchain data client for '{}' with HTTP RPC: {}",
            chain.name,
            REDACTED
        );

        let rpc_client = if !config.use_hypersync_for_live_data && config.wss_rpc_url.is_some() {
            let wss_rpc_url = config.wss_rpc_url.clone().expect("wss_rpc_url is required");
            log::debug!("WebSocket RPC URL: {REDACTED}");
            Some(Self::initialize_rpc_client(
                chain.name,
                wss_rpc_url.into_inner(),
                config.transport_backend,
                config.proxy_url.clone().map(|value| value.into_inner()),
            ))
        } else {
            log::debug!("Using HyperSync for live data (no WebSocket RPC)");
            None
        };
        let http_rpc_client = Arc::new(BlockchainHttpRpcClient::new(
            config.http_rpc_url.clone().into_inner(),
            config.rpc_requests_per_second,
            config.proxy_url.clone().map(|value| value.into_inner()),
        ));
        let multicall_calls_per_rpc_request = config.multicall_calls_per_rpc_request;
        let erc20_contract = Erc20Contract::new(

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Set `wss_rpc_url` (e.g. `wss://mainnet.infura.io/ws/v3/<key>`) in the client config when HyperSync live data is disabled.
  2. Alternatively enable `use_hypersync_for_live_data = true` so the WSS URL is not required.
  3. Check the config key spelling/section so the URL actually deserializes into `wss_rpc_url`.
  4. Validate the config (wss URL present iff not using HyperSync) before calling `new`.

Example fix

// before
let config = BlockchainDataClientConfig {
    use_hypersync_for_live_data: false,
    wss_rpc_url: None, // panics in new()
    ..Default::default()
};
// after
let config = BlockchainDataClientConfig {
    use_hypersync_for_live_data: false,
    wss_rpc_url: Some(SecretString::from("wss://mainnet.infura.io/ws/v3/<key>".to_string())),
    ..Default::default()
};
Defensive patterns

Strategy: validation

Validate before calling

if !config.use_hypersync_for_live_data && config.wss_rpc_url.is_none() {
    return Err(anyhow!("wss_rpc_url is required when use_hypersync_for_live_data is false"));
}

Type guard

fn wss_ready(config: &BlockchainDataClientConfig) -> bool {
    config.use_hypersync_for_live_data || config.wss_rpc_url.is_some()
}

Try / catch

// Rust: check the option yourself before constructing
let wss = match (&config.use_hypersync_for_live_data, &config.wss_rpc_url) {
    (false, Some(url)) => Some(url.clone()),
    (false, None) => return Err(anyhow!("wss_rpc_url is required")),
    (true, _) => None,
};

Prevention

When it happens

Trigger: Constructing the client with `use_hypersync_for_live_data = false` and `wss_rpc_url = None` (or a config that deserializes it as None while another thread mutates it), reaching the `.expect` on the cloned Option.

Common situations: Config file or env omitting the WSS RPC URL while live-data mode requires it; typo'd config key so the field defaults to None; config builder leaving the optional field unset.

Understand the failure class

Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.

Related errors


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