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

  1. Recreate a new WorkerPool instead of reusing the closed one.
  2. Check `pool.closed` (or track lifecycle) before calling parse.
  3. Reorder shutdown logic so the pool closes only after all pending parses finish (await outstanding promises before close()).
  4. 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

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


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)