{"record":{"id":"08fa512031112616","repo":"nautechsystems/nautilus_trader","slug":"spot-public-json-stream-pool-exhausted-max-conne","errorCode":null,"errorMessage":"Spot public JSON stream pool exhausted ({MAX_CONNECTIONS} connections x {MAX_STREAMS_PER_CONNECTION} streams)","messagePattern":"Spot public JSON stream pool exhausted \\((.+?) connections x (.+?) streams\\)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/adapters/binance/src/spot/websocket/public_json/client.rs","lineNumber":378,"sourceCode":"                self.url\n            );\n        }\n\n        // Phase 3: stage assignments, send commands, then commit slot state.\n        let mut 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        let mut slot_batches: Vec<(usize, Vec<String>)> = Vec::new();\n        let mut slot_counts: Vec<usize> = slots.iter().map(|s| s.streams.len()).collect();\n\n        for stream in &new_streams {\n            let slot_idx = slot_counts\n                .iter()\n                .position(|&count| count < MAX_STREAMS_PER_CONNECTION)\n                .ok_or_else(|| {\n                    anyhow::anyhow!(\n                        \"Spot public JSON stream pool exhausted ({MAX_CONNECTIONS} connections x {MAX_STREAMS_PER_CONNECTION} streams)\",\n                    )\n                })?;\n\n            slot_counts[slot_idx] += 1;\n\n            if let Some(batch) = slot_batches.iter_mut().find(|(i, _)| *i == slot_idx) {\n                batch.1.push(stream.clone());\n            } else {\n                slot_batches.push((slot_idx, vec![stream.clone()]));\n            }\n        }\n\n        for (slot_idx, batch) in &slot_batches {\n            slots[*slot_idx]\n                .cmd_tx\n                .send(BinanceSpotPublicWsCommand::Subscribe {\n                    streams: batch.clone(),","sourceCodeStart":360,"sourceCodeEnd":396,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/binance/src/spot/websocket/public_json/client.rs#L360-L396","documentation":"The pool has a hard capacity of MAX_CONNECTIONS WebSocket connections, each carrying at most MAX_STREAMS_PER_CONNECTION streams (Binance's limits). subscribe() failed because every existing slot is full and no free slot exists to host the new streams.","triggerScenarios":"Calling subscribe() with new streams when all pool slots already hold MAX_STREAMS_PER_CONNECTION streams and MAX_CONNECTIONS connections are open.","commonSituations":"Subscribing to very large instrument universes (hundreds/thousands of symbols) on Binance Spot, exceeding the 1024-streams-per-connection / limited-connections budget; forgetting to unsubscribe stale streams before adding new ones.","solutions":["Reduce the number of simultaneously subscribed streams; unsubscribe instruments no longer needed before subscribing new ones.","Split the workload across multiple pool/client instances (e.g., per asset class or shard).","Check MAX_STREAMS_PER_CONNECTION/MAX_CONNECTIONS constants against current Binance limits and adjust configuration if they are outdated.","Handle this error by degrading: fall back to REST polling or a reduced symbol set when the pool is exhausted."],"exampleFix":"// before\nclient.subscribe(huge_stream_list).await?; // may exceed pool capacity\n\n// after\nlet capacity = MAX_CONNECTIONS * MAX_STREAMS_PER_CONNECTION;\nif client.active_stream_count() + huge_stream_list.len() > capacity {\n    client.unsubscribe(stale_streams).await?;\n}\nclient.subscribe(huge_stream_list).await?;","handlingStrategy":"validation","validationCode":"const POOL_CAPACITY: usize = MAX_CONNECTIONS * MAX_STREAMS_PER_CONNECTION;\nif client.active_stream_count() + new_streams.len() > POOL_CAPACITY {\n    // trim, shard, or unsubscribe first\n}","typeGuard":null,"tryCatchPattern":"match client.subscribe(streams).await {\n    Err(e) if e.to_string().contains(\"pool exhausted\") => {\n        // degrade: reduce symbol set or fall back to REST polling\n    }\n    r => r?,\n}","preventionTips":["Track active stream counts against MAX_CONNECTIONS * MAX_STREAMS_PER_CONNECTION before subscribing.","Unsubscribe stale instruments before adding new ones.","Shard large universes across multiple clients/instances.","Keep adapter constants in sync with current Binance Spot limits."],"tags":["capacity-limit","websocket","subscription","resource-exhaustion"],"backgroundTag":"value-out-of-range","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"}