run-llama/liteparse · error · Error
parser pool is closed
Error message
parser pool is closed
What it means
Once close() has been called on a WorkerPool, all workers are shut down and parse() refuses new work by throwing 'parser pool is closed'. Using a pool after closing it is a lifecycle programming error, so the library fails fast rather than hanging on a dead worker.
Solutions
- Recreate a new WorkerPool instead of reusing the closed one.
- Check `pool.closed` (or track lifecycle) before calling parse.
- Reorder shutdown logic so the pool closes only after all pending parses finish (await outstanding promises before close()).
- Inject the pool via a getter that lazily (re)creates it rather than caching a closed instance.
Example fix
// before await pool.close(); const r = await pool.parse(buf, 'doc.pdf'); // after await pool.close(); pool = new WorkerPool(cfg, poolSize, timeoutMs); const r = await pool.parse(buf, 'doc.pdf');
Defensive patterns
Strategy: try-catch
Validate before calling
if (pool.closed) {
pool = new WorkerPool(config, poolSize, timeoutMs);
}
await pool.parse(buf, 'doc.pdf'); Type guard
const isUsable = (p) => p != null && p.closed !== true;
Try / catch
try {
await pool.parse(buf, 'doc.pdf');
} catch (e) {
if (e.message === 'parser pool is closed') {
pool = new WorkerPool(config, poolSize, timeoutMs);
return pool.parse(buf, 'doc.pdf');
}
throw e;
} Prevention
- Centralize pool lifecycle in one owner module; other code asks it for a live pool.
- On server shutdown, drain in-flight requests before calling close().
- In tests, recreate the pool per test instead of sharing closed instances.
When it happens
Trigger: Calling `await pool.parse(...)` after `await pool.close()`; keeping a module-level pool reference that some shutdown handler already closed; a request arriving in a server after graceful shutdown closed the pool; awaiting a long queue while another code path closes the pool concurrently.
Common situations: Server shutdown handlers closing the pool while in-flight requests still enqueue; tests tearing down a shared pool in beforeEach; hot-reload in dev closing the old pool while stale references remain.
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
- parseTimeoutMs requires poolSize
- poolSize must be an integer >= 1
- parseTimeoutMs must be > 0
- parse of exceeded ms; the worker process was killed
- liteparse worker process died while parsing
AI-assisted analysis of run-llama/liteparse@22d2dd8cd7 (2026-09-08).
Data as JSON: /api/errors/f54478e7e2d4e9f7.
Report an issue: GitHub.
Appendix: source
Thrown at packages/node/src/pool.ts:252
return;
}
const waiter = this.waiters.shift();
if (waiter !== undefined) waiter.resolve(worker);
else this.idle.push(worker);
}
private retire(worker: WorkerHandle): void {
worker.kill();
this.workers.delete(worker);
if (!this.closed) this.spawnWorker();
}
/** Run one parse on an idle worker.
*
* Waits for a free worker first; `parseTimeoutMs` bounds the parse itself,
* not the wait. */
async parse(payload: string | Buffer, source: string): Promise<ParseResult> {
if (this.closed) throw new Error("parser pool is closed");
const worker = await this.acquire();
try {
await worker.ready();
const result = await worker.request(payload, this.timeoutMs);
this.release(worker);
return result;
} catch (e) {
this.retire(worker);
if (e instanceof WorkerTimeout) {
throw new ParseTimeoutError(
`parse of ${source} exceeded ${this.timeoutMs}ms; the worker process was killed`,
source,
this.timeoutMs!,
);
}
if (e instanceof WorkerCrashed) {
throw new Error(
`liteparse worker process died while parsing ${source}: ${e.message}`,View on GitHub (pinned to 22d2dd8cd7)