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
- Create a new WorkerPool after close() if more parsing is needed — pools cannot be reopened
- Reorder code so all parse calls happen before close(), and only close in the final teardown/finally path
- Structure code with a context manager so the pool scope encloses all parse usage
- Check pool state before parsing if your wrapper exposes it, and lazily recreate the pool on demand
- 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
- Own the pool lifecycle in one place (context manager or app shutdown hook)
- Never call pool.close() in per-request cleanup in servers
- Lazily recreate the pool on ParseError 'closed' if a reopen path is acceptable
- Guard against signal/atexit handlers racing in-flight parses
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
- parse of exceeded s; the worker process was killed
- parser pool is closed
- pool_size must be >= 1
- parse_timeout must be > 0 seconds
- parse_timeout requires pool_size
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 NoneView on GitHub (pinned to 22d2dd8cd7)