{"record":{"id":"5e4d085f87ce6b3d","repo":"nautechsystems/nautilus_trader","slug":"binance-spot-public-json-stream-pool-is-shutting-d","errorCode":null,"errorMessage":"Binance Spot public JSON stream pool is shutting down","messagePattern":"Binance Spot public JSON stream pool is shutting down","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"crates/adapters/binance/src/spot/websocket/public_json/client.rs","lineNumber":310,"sourceCode":"        }\n        log::debug!(\"Disconnected from Binance Spot public JSON stream pool\");\n        Ok(())\n    }\n\n    /// Subscribes to stream names.\n    ///\n    /// # Errors\n    ///\n    /// Returns an error if command delivery fails or if the connection pool is exhausted.\n    pub async fn subscribe(&self, streams: Vec<String>) -> anyhow::Result<()> {\n        let _connect_guard = self.connect_lock.lock().await;\n\n        // Phase 1: filter already-subscribed streams (brief lock)\n        let new_streams: Vec<String> = {\n            let slots = self.slots.lock();\n\n            if self.signal.load(Ordering::Acquire) {\n                anyhow::bail!(\"Binance Spot public JSON stream pool is shutting down\");\n            }\n            streams\n                .into_iter()\n                .filter(|s| !slots.iter().any(|slot| slot.streams.contains(s)))\n                .collect()\n        };\n\n        if new_streams.is_empty() {\n            return Ok(());\n        }\n\n        // Phase 2: create connections if needed (no lock held during async connect)\n        loop {\n            let (remaining_capacity, slot_count) = {\n                let slots = self.slots.lock();\n                let cap: usize = slots\n                    .iter()\n                    .map(|s| MAX_STREAMS_PER_CONNECTION.saturating_sub(s.streams.len()))","sourceCodeStart":292,"sourceCodeEnd":328,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/binance/src/spot/websocket/public_json/client.rs#L292-L328","documentation":"subscribe checks the pool's shutdown signal (atomic flag) while filtering already-subscribed streams under the slots lock; if the pool is shutting down it refuses new subscriptions. This is phase 1 of a three-phase subscribe protocol, guarding against subscribing to a closing pool.","triggerScenarios":"Calling subscribe (public) concurrently with close_connections/close: the shutdown signal was set (Ordering::Acquire observed true) before or during the phase-1 filtering, so any new stream subscription is rejected.","commonSituations":"A racing task subscribes while the user closes the client; tests intentionally exercising shutdown races; application shutdown ordering where a data consumer keeps subscribing after stream teardown begins.","solutions":["Stop issuing subscribe calls before closing the pool; cancel producer tasks that call subscribe during shutdown.","Treat the error as benign during application shutdown and swallow it where appropriate.","Sequence shutdown: unsubscribe/close all consumers first, then call close.","If this occurs unexpectedly, check for leaked background tasks still calling subscribe after close was initiated."],"exampleFix":"// before\nhandle.subscribe(streams).await?;\n// after\nif app_is_shutting_down() {\n    log::debug!(\"skip subscribe, pool shutting down\");\n} else {\n    handle.subscribe(streams).await?;\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"match handle.subscribe(streams).await {\n    Err(e) if e.to_string().contains(\"shutting down\") => log::debug!(\"pool closed, skipping subscribe\"),\n    other => other?,\n}","preventionTips":["Cancel all producer tasks that call subscribe before closing the pool.","Use a lifecycle flag in your app so subscriptions stop before teardown starts.","Sequence close strictly after all consumers have finished subscribing."],"tags":["websocket","binance","spot","shutdown-race","lifecycle"],"backgroundTag":"invalid-state-transition","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}