nautechsystems/nautilus_trader · error

Market connection pool is closed; shard rollback failed: {e}

Error message

Market connection pool is closed; shard rollback failed: {e}

What it means

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.

Source

Thrown at crates/adapters/polymarket/src/websocket/pool.rs:610

                    state
                        .shards
                        .remove(&id)
                        .expect("new shard retained for shutdown"),
                )
            } else {
                let handle = {
                    let shard = state.shards.get_mut(&id).expect("new shard present");
                    shard.owned += 1;
                    shard.handle.clone()
                };
                state.assignments.insert(token, id);
                return Ok(Some(handle));
            }
        };

        if let Some(shard) = rejected_shard {
            if let Err(e) = self.close_shard(id, Box::new(shard)).await {
                anyhow::bail!("Market connection pool is closed; shard rollback failed: {e}");
            }
            anyhow::bail!("Market connection pool is closed");
        }
        unreachable!("open pool returned from assignment")
    }

    fn release(&self, token: Ustr) -> ReleaseOutcome {
        let mut state = self.state.lock();

        let Some(id) = state.assignments.remove(&token) else {
            return ReleaseOutcome::NotOwned;
        };

        let owned = {
            let Some(shard) = state.shards.get_mut(&id) else {
                return ReleaseOutcome::NotOwned;
            };
            shard.owned = shard.owned.saturating_sub(1);

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Treat the pool as closed regardless of the rollback error — recreate and reconnect
  2. Log/inspect the chained {e} for why shard cleanup failed (usually a dead connection)
  3. Ensure shutdown tolerates already-failed shards so rollback closes don't add noise
  4. Avoid concurrent disconnect and subscribe on the same pool
Defensive patterns

Strategy: try-catch

Try / catch

if let Err(e) = pool.subscribe_one(asset_id).await {
    if e.to_string().contains("pool is closed") {
        // chained rollback failure is informational; recreate the pool
        let pool = reconnect_pool()?;
    }
}

Prevention

When it happens

Trigger: 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).

Common situations: Shutdown racing an in-flight subscribe; shard connections already broken when the rollback close runs.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


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