nautechsystems/nautilus_trader · error

RPC block feed active without an RPC client

Error message

RPC block feed active without an RPC client

What it means

unsubscribe_block_feed sees an active RPC block feed backend but the core client's rpc_client is None, which is an inconsistent state — a live RPC subscription exists without the client that owns it. It bails rather than silently skipping cleanup.

Source

Thrown at crates/adapters/blockchain/src/data/client.rs:1103

        core_client
            .subscription_manager
            .block_feed_started(started_backend);
        Ok(())
    }

    async fn unsubscribe_block_feed(
        core_client: &mut BlockchainDataClientCore,
        owner: BlockFeedOwner,
    ) -> anyhow::Result<()> {
        let Some(backend) = core_client.subscription_manager.remove_block_demand(owner) else {
            log::debug!("Keeping block subscription active while another owner remains");
            return Ok(());
        };

        match backend {
            BlockFeedBackend::Rpc => {
                let Some(rpc) = core_client.rpc_client.as_mut() else {
                    anyhow::bail!("RPC block feed active without an RPC client")
                };
                rpc.unsubscribe_blocks().await?;
                log::debug!("Unsubscribed from blocks via RPC");
            }
            BlockFeedBackend::HyperSync => {
                core_client.hypersync_client.unsubscribe_blocks().await;
                log::debug!("Unsubscribed from blocks via HyperSync");
            }
        }

        core_client.subscription_manager.block_feed_stopped(backend);
        Ok(())
    }

    /// Processes DeFi request commands to fetch specific blockchain data.
    async fn handle_request_command(
        command: DefiRequestCommand,
        core_client: &mut BlockchainDataClientCore,

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Ensure the same client instance/config that subscribed (with rpc_client present) performs the unsubscribe
  2. Re-initialize the RPC client before unsubscribing
  3. If switching backends intentionally, unsubscribe with the correct backend selection

Example fix

// before
// client recreated without rpc url; unsubscribe_block_feed() fails
// after
client.reconnect_with_rpc("wss://...")?; // restores rpc_client
client.unsubscribe_block_feed().await?;
Defensive patterns

Strategy: type-guard

Validate before calling

if backend == BlockFeedBackend::Rpc && client.rpc_client().is_none() {
    log::warn!("rpc client gone; skipping rpc block unsubscribe");
    return;
}

Type guard

fn rpc_available(c: &BlockchainDataClient) -> bool { c.core.rpc_client.is_some() }

Try / catch

match client.unsubscribe_block_feed().await {
    Err(e) if e.to_string().contains("active without an RPC client") => {
        log::warn!("stale rpc backend state; resetting block feed state");
    }
    r => r?,
}

Prevention

When it happens

Trigger: Calling unsubscribe_block_feed when the tracked backend is BlockFeedBackend::Rpc but the client was rebuilt/reconnected without rpc_client, or the backend record was set to Rpc while rpc_client was dropped.

Common situations: Client re-initialization between subscribe and unsubscribe; mixing backend configurations across reconnects; internal state corruption after an RPC client teardown.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


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