run-llama/liteparse · error · ParseError

parser pool is closed

Error message

parser pool is closed

What it means

WorkerPool.parse raises this ParseError once close() has been called on the pool: all worker processes were shut down and the pool no longer accepts parse requests. It guards against using a terminated pool rather than lazily respawning workers.

Solutions

  1. Create a new WorkerPool after close() if more parsing is needed — pools cannot be reopened
  2. Reorder code so all parse calls happen before close(), and only close in the final teardown/finally path
  3. Structure code with a context manager so the pool scope encloses all parse usage
  4. Check pool state before parsing if your wrapper exposes it, and lazily recreate the pool on demand
  5. In long-running services, avoid closing the pool in per-request cleanup; only close on application shutdown

Example fix

# before
pool = WorkerPool(config, pool_size=2)
pool.close()
result = pool.parse(data, 'doc.pdf')  # ParseError: parser pool is closed
# after
pool = WorkerPool(config, pool_size=2)
try:
    result = pool.parse(data, 'doc.pdf')
finally:
    pool.close()
Defensive patterns

Strategy: try-catch

Validate before calling

# If your wrapper exposes state:
if getattr(pool, '_closed', False):
    pool = WorkerPool(config, pool_size=pool_size)  # recreate instead of parsing

Type guard

def pool_is_usable(pool: WorkerPool) -> bool:
    return not getattr(pool, '_closed', True)

Try / catch

try:
    result = pool.parse(data, source)
except ParseError as e:
    if 'parser pool is closed' in str(e):
        pool = WorkerPool(config, pool_size=POOL_SIZE)
        result = pool.parse(data, source)
    else:
        raise

Prevention

When it happens

Trigger: Calling pool.parse(...) after pool.close() (or after a context manager / shutdown path already ran), often due to reuse of a long-lived pool object across requests, calling parse from a callback after shutdown, or an atexit/signal handler closing the pool while work is still queued.

Common situations: Web server workers shutting the pool down at process exit while a request is still in flight; unit tests that close a module-level pool then run another test reusing it; scripts that call close() in a finally block then attempt a retry parse.

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/e16d6542353bb349. Report an issue: GitHub.

Appendix: source

Thrown at packages/python/liteparse/_pool.py:237

        worker = _Worker(self._config)
        with self._lock:
            self._workers.append(worker)
        self._idle.put(worker)

    def _retire_worker(self, worker: _Worker) -> None:
        worker.kill()
        with self._lock:
            if worker in self._workers:
                self._workers.remove(worker)

    def parse(self, payload: Union[str, bytes], source: str) -> Any:
        """Run one parse on an idle worker.

        Blocks until a worker is free; ``parse_timeout`` bounds the parse
        itself, not the wait for a free worker.
        """
        if self._closed:
            raise ParseError("parser pool is closed")
        worker = self._idle.get()
        replace = False
        try:
            status, data = worker.request(payload, self._timeout)
        except _WorkerTimeout:
            replace = True
            timeout = self._timeout
            raise ParseTimeoutError(
                f"parse of {source} exceeded {timeout}s; "
                "the worker process was killed",
                source=source,
                timeout=timeout,
            ) from None
        except _WorkerCrashed as e:
            replace = True
            raise ParseError(
                f"liteparse worker process died while parsing {source}: {e}"
            ) from None

View on GitHub (pinned to 22d2dd8cd7)