{"record":{"id":"e16d6542353bb349","repo":"run-llama/liteparse","slug":"parser-pool-is-closed-e16d65","errorCode":null,"errorMessage":"parser pool is closed","messagePattern":"parser pool is closed","errorType":"exception","errorClass":"ParseError","httpStatus":null,"severity":"error","filePath":"packages/python/liteparse/_pool.py","lineNumber":237,"sourceCode":"        worker = _Worker(self._config)\n        with self._lock:\n            self._workers.append(worker)\n        self._idle.put(worker)\n\n    def _retire_worker(self, worker: _Worker) -> None:\n        worker.kill()\n        with self._lock:\n            if worker in self._workers:\n                self._workers.remove(worker)\n\n    def parse(self, payload: Union[str, bytes], source: str) -> Any:\n        \"\"\"Run one parse on an idle worker.\n\n        Blocks until a worker is free; ``parse_timeout`` bounds the parse\n        itself, not the wait for a free worker.\n        \"\"\"\n        if self._closed:\n            raise ParseError(\"parser pool is closed\")\n        worker = self._idle.get()\n        replace = False\n        try:\n            status, data = worker.request(payload, self._timeout)\n        except _WorkerTimeout:\n            replace = True\n            timeout = self._timeout\n            raise ParseTimeoutError(\n                f\"parse of {source} exceeded {timeout}s; \"\n                \"the worker process was killed\",\n                source=source,\n                timeout=timeout,\n            ) from None\n        except _WorkerCrashed as e:\n            replace = True\n            raise ParseError(\n                f\"liteparse worker process died while parsing {source}: {e}\"\n            ) from None","sourceCodeStart":219,"sourceCodeEnd":255,"githubUrl":"https://github.com/run-llama/liteparse/blob/22d2dd8cd7f7b9320102b57ddaf0e663ff7d15a8/packages/python/liteparse/_pool.py#L219-L255","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"# before\npool = WorkerPool(config, pool_size=2)\npool.close()\nresult = pool.parse(data, 'doc.pdf')  # ParseError: parser pool is closed\n# after\npool = WorkerPool(config, pool_size=2)\ntry:\n    result = pool.parse(data, 'doc.pdf')\nfinally:\n    pool.close()","handlingStrategy":"try-catch","validationCode":"# If your wrapper exposes state:\nif getattr(pool, '_closed', False):\n    pool = WorkerPool(config, pool_size=pool_size)  # recreate instead of parsing","typeGuard":"def pool_is_usable(pool: WorkerPool) -> bool:\n    return not getattr(pool, '_closed', True)","tryCatchPattern":"try:\n    result = pool.parse(data, source)\nexcept ParseError as e:\n    if 'parser pool is closed' in str(e):\n        pool = WorkerPool(config, pool_size=POOL_SIZE)\n        result = pool.parse(data, source)\n    else:\n        raise","preventionTips":["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"],"tags":["python","lifecycle","resource-closed","concurrency"],"backgroundTag":"invalid-state-transition","analyzedSha":"22d2dd8cd7f7b9320102b57ddaf0e663ff7d15a8","analyzedAt":"2026-09-08T06:09:49.009Z","contentChangedAt":"2026-09-08T06:09:49.009Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}