{"record":{"id":"b07f8bf69f06af28","repo":"nautechsystems/nautilus_trader","slug":"market-connection-pool-is-closed-shard-rollback-f","errorCode":null,"errorMessage":"Market connection pool is closed; shard rollback failed: {e}","messagePattern":"Market connection pool is closed; shard rollback failed: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/adapters/polymarket/src/websocket/pool.rs","lineNumber":610,"sourceCode":"                    state\n                        .shards\n                        .remove(&id)\n                        .expect(\"new shard retained for shutdown\"),\n                )\n            } else {\n                let handle = {\n                    let shard = state.shards.get_mut(&id).expect(\"new shard present\");\n                    shard.owned += 1;\n                    shard.handle.clone()\n                };\n                state.assignments.insert(token, id);\n                return Ok(Some(handle));\n            }\n        };\n\n        if let Some(shard) = rejected_shard {\n            if let Err(e) = self.close_shard(id, Box::new(shard)).await {\n                anyhow::bail!(\"Market connection pool is closed; shard rollback failed: {e}\");\n            }\n            anyhow::bail!(\"Market connection pool is closed\");\n        }\n        unreachable!(\"open pool returned from assignment\")\n    }\n\n    fn release(&self, token: Ustr) -> ReleaseOutcome {\n        let mut state = self.state.lock();\n\n        let Some(id) = state.assignments.remove(&token) else {\n            return ReleaseOutcome::NotOwned;\n        };\n\n        let owned = {\n            let Some(shard) = state.shards.get_mut(&id) else {\n                return ReleaseOutcome::NotOwned;\n            };\n            shard.owned = shard.owned.saturating_sub(1);","sourceCodeStart":592,"sourceCodeEnd":628,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/polymarket/src/websocket/pool.rs#L592-L628","documentation":"When the pool is detected as closed during assignment (after a rejected shard attempt left `rejected_shard` set), the code attempts to close the now-orphaned shard. If that close_shard call fails, the closed-pool condition and the shard-cleanup failure are combined into this error instead of the plain closed-pool error.","triggerScenarios":"Pool closes concurrently with assign(); assignment is rejected by a shard that must then be closed, and close_shard on that rejected shard returns Err (e.g., connection already dead or close task fails).","commonSituations":"Shutdown racing an in-flight subscribe; shard connections already broken when the rollback close runs.","solutions":["Treat the pool as closed regardless of the rollback error — recreate and reconnect","Log/inspect the chained {e} for why shard cleanup failed (usually a dead connection)","Ensure shutdown tolerates already-failed shards so rollback closes don't add noise","Avoid concurrent disconnect and subscribe on the same pool"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"if let Err(e) = pool.subscribe_one(asset_id).await {\n    if e.to_string().contains(\"pool is closed\") {\n        // chained rollback failure is informational; recreate the pool\n        let pool = reconnect_pool()?;\n    }\n}","preventionTips":["Stop subscriptions at shutdown initiation","Make shard cleanup tolerant of dead connections","Own pool lifecycle in a single task","Log chained rollback errors for diagnosis"],"tags":["websocket","polymarket","race-condition","cleanup"],"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"}