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
- Increase the pool's maxSize / acquire timeout options in the Pool constructor config.
- Fix resource leaks: ensure every acquire is paired with a release, or prefer pool.use(cb) which auto-releases.
- Reduce concurrency (query orchestrator limits, queues) so demand fits pool capacity.
- 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
- Size the pool (maxSize) for peak concurrency, not average.
- Always release acquired resources; prefer pool.use(cb) for auto-release.
- Set an explicit acquire timeout appropriate to workload.
- Monitor pool wait times and alert before timeouts occur.
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
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- Athena job timeout reached ${this.config.pollTimeout}ms
- BigQuery job timeout reached ${this.options.pollTimeout}ms
- CubeSQL query timed out after ${timeoutMs}ms
- DremioQuery job timeout reached ${this.config.pollTimeout}ms
- nothing is building branch {branch} (status {status}{}). If
AI-assisted analysis of cube-js/cube@7d981676b3 (2026-09-02).
Data as JSON: /api/errors/8680e5b899ffaf2b.
Report an issue: GitHub.