cube-js/cube · error · PoolTimeoutError

PoolTimeoutError

Error message

PoolTimeoutError

What it means

PoolTimeoutError is thrown by the generic resource-pool wrapper's acquire() when the underlying pool's acquire() rejects with an error named 'TimeoutError' — i.e. no resource became available within the pool's configured acquisition timeout. It is a normalized error type so callers can reliably identify pool-exhaustion timeouts. The original TimeoutError is replaced by PoolTimeoutError carrying the pool name.

Source

Thrown at packages/cubejs-backend-shared/src/pool.ts:44

 * Uses composition instead of inheritance because generic-pool doesn't export
 * a Pool class, the Pool type is an interface, not an extendable class.
 */
export class Pool<T> {
  private readonly pool: GenericPool<T>;

  private readonly name: string;

  public constructor(name: string, factory: PoolFactory<T>, options?: Options) {
    this.name = name;
    this.pool = genericPool.createPool<T>(factory, options);
  }

  public async acquire(priority?: number): Promise<T> {
    try {
      return await this.pool.acquire(priority);
    } catch (error) {
      if (error instanceof Error && error.name === 'TimeoutError') {
        throw new PoolTimeoutError(this.name);
      }

      throw error;
    }
  }

  public async release(resource: T): Promise<void> {
    return this.pool.release(resource);
  }

  public async destroy(resource: T): Promise<void> {
    return this.pool.destroy(resource);
  }

  public async drain(): Promise<void> {
    return this.pool.drain();
  }

View on GitHub (pinned to 7d981676b3)

Solutions

  1. Increase the pool's maxSize / acquire timeout options in the Pool constructor config.
  2. Fix resource leaks: ensure every acquire is paired with a release, or prefer pool.use(cb) which auto-releases.
  3. Reduce concurrency (query orchestrator limits, queues) so demand fits pool capacity.
  4. Catch PoolTimeoutError specifically and apply backoff/retry at the call site.

Example fix

// before
const pool = new Pool(clientFactory, { max: 5 });
// after
const pool = new Pool(clientFactory, { max: 20, acquireTimeout: 30000 });
Defensive patterns

Strategy: retry

Validate before calling

// pre-check pool saturation if the pool exposes state accessors
// e.g. if (pool.getBusyCount?.() >= maxSize) await waitForFreeSlot();

Type guard

import { PoolTimeoutError } from '@cubejs-backend/shared';
const isPoolTimeout = (e: unknown): e is PoolTimeoutError => e instanceof PoolTimeoutError;

Try / catch

try {
  const client = await pool.acquire();
} catch (e) {
  if (e instanceof PoolTimeoutError) {
    // backoff and retry acquisition
  } else {
    throw e;
  }
}

Prevention

When it happens

Trigger: Calling acquire() (or code that uses it) while all pooled resources are checked out and the wait exceeds the pool's acquire timeout; e.g. too many concurrent queries for a small maxSize pool, or leaked resources never released.

Common situations: Database/driver pools sized too small for concurrent load; resources not returned because callers forget to release; long-running operations holding connections longer than the timeout; load spikes during heavy orchestration queries.

Understand the failure class

Related errors


AI-assisted analysis of cube-js/cube@7d981676b3 (2026-09-02). Data as JSON: /api/errors/8680e5b899ffaf2b. Report an issue: GitHub.