ruvnet/ruflo · error · Error
Connection pool is shutting down
Error message
Connection pool is shutting down
What it means
ConnectionPool.acquire() checks isShuttingDown and refuses new checkouts once shutdown() has begun — the pool is draining existing connections and hands out nothing new. Any acquire that races teardown gets this error.
Source
Thrown at v3/@claude-flow/mcp/src/connection-pool.ts:124
private async createConnection(): Promise<ManagedConnection> {
const id = `conn-${++this.connectionCounter}-${Date.now()}`;
const connection = new ManagedConnection(id, this.transportType);
this.connections.set(id, connection);
this.stats.totalCreated++;
this.emit('pool:connection:created', { connectionId: id });
this.logger.debug('Connection created', { id, total: this.connections.size });
return connection;
}
async acquire(): Promise<PooledConnection> {
const startTime = performance.now();
if (this.isShuttingDown) {
throw new Error('Connection pool is shutting down');
}
for (const connection of this.connections.values()) {
if (connection.state === 'idle' && connection.isHealthy()) {
connection.acquire();
this.stats.totalAcquired++;
this.recordAcquireTime(startTime);
this.emit('pool:connection:acquired', { connectionId: connection.id });
this.logger.debug('Connection acquired from pool', { id: connection.id });
return connection;
}
}
if (this.connections.size < this.config.maxConnections) {
const connection = await this.createConnection();
connection.acquire();View on GitHub (pinned to fa13ee4ad6)
Solutions
- Fix teardown order: stop accepting/producing work, await in-flight tasks, then call pool.shutdown()
- In late-running code, catch this error and skip — acquiring during a drain is a logic bug, not a retryable fault
- Gate connection use on your own draining flag set before pool.shutdown()
Example fix
// before
process.on('SIGTERM', () => pool.shutdown());
setInterval(work, 1000); // work() calls pool.acquire() -> throws during drain
// after
let draining = false;
const timer = setInterval(work, 1000);
process.on('SIGTERM', () => {
draining = true;
clearInterval(timer);
pool.shutdown();
});
async function work() {
if (draining) return;
const conn = await pool.acquire();
// ...
} Defensive patterns
Strategy: try-catch
Validate before calling
// Flip your own gate before initiating shutdown
let draining = false;
async function shutdown() {
draining = true;
await inFlight; // let consumers finish
await pool.shutdown();
}
async function withConnection<T>(fn: (c: PooledConnection) => Promise<T>): Promise<T> {
if (draining) throw new Error('app draining');
return fn(await pool.acquire());
} Try / catch
try {
const conn = await pool.acquire();
} catch (e) {
if (e instanceof Error && e.message === 'Connection pool is shutting down') {
return; // we're draining — skip this work item entirely, never retry
}
throw e;
} Prevention
- Shutdown order: stop producers, drain consumers, then close the pool
- Never call pool.shutdown() from a signal handler without first stopping timers and background work
- Watch the pool's shutdown events to confirm a clean drain before process exit
When it happens
Trigger: Calling acquire() after pool.shutdown() started: typically in-flight request handlers, timers, or background jobs that still run during process or service shutdown and attempt a checkout.
Common situations: A SIGTERM handler closing the pool while requests are still draining; a timer firing mid-shutdown; cleanup ordering bugs where shutdown runs before the last consumers finish.
Related errors
- Connection pool is shutting down
- Connection pool already initialized
- Connection pool not initialized
- FederationTransport: closed
- Can only resume paused agent
AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18).
Data as JSON: /api/errors/42897d628b95d1e6.
Report an issue: GitHub.