{"record":{"id":"d6d6cfd95ee5e15b","repo":"zylon-ai/private-gpt","slug":"task-did-not-complete-within-self-poll-timeout-s","errorCode":null,"errorMessage":"Task did not complete within {self.poll_timeout} seconds","messagePattern":"Task did not complete within (.+?) seconds","errorType":"exception","errorClass":"TimeoutError","httpStatus":null,"severity":"error","filePath":"private_gpt/components/readers/docling/api_clients.py","lineNumber":502,"sourceCode":"            response.raise_for_status()\n            result = await response.json()\n            return DoclingApiOutputModel(**result)\n\n    async def _wait_for_completion(self, task_id: str) -> DoclingApiOutputModel:\n        start_time = time.time()\n        while not self.poll_timeout or time.time() - start_time < self.poll_timeout:\n            status = await self._poll_task_status(task_id)\n            if status.task_status == \"success\":\n                task_result: DoclingApiOutputModel = await self._get_task_result(\n                    task_id\n                )\n                return task_result\n            if status.task_status in [\"failure\", \"skipped\"]:\n                raise ValueError(f\"Task failed with status: {status.task_status}\")\n\n            await asyncio.sleep(self.poll_interval)\n\n        raise TimeoutError(f\"Task did not complete within {self.poll_timeout} seconds\")\n\n    @retry(\n        is_async=True,\n        tries=_MAX_RETRIES,\n        jitter=_JITTER,\n        logger=logger,\n        exceptions=ResourceNotFoundError,\n    )\n    async def convert_from_bytes(\n        self, file_name: str, file_bytes: bytes, **kwargs: Any\n    ) -> DoclingApiOutputModel:\n        task_id = await self._submit_task(file_name, file_bytes, **kwargs)\n        return await self._wait_for_completion(task_id)\n\n\nclass DoclingClientFactory:\n    @staticmethod\n    def create(config: DoclingConfig, async_client: bool = False) -> BaseDoclingClient:","sourceCodeStart":484,"sourceCodeEnd":520,"githubUrl":"https://github.com/zylon-ai/private-gpt/blob/4a030776a31a901ad80b1bf4d7faa2c1a367efbb/private_gpt/components/readers/docling/api_clients.py#L484-L520","documentation":"Raised by AsyncDoclingClient._wait_for_completion when the polling loop exceeds poll_timeout seconds without the task reaching success/failure/skipped. Important subtlety from the loop condition `while not self.poll_timeout or time.time() - start_time < self.poll_timeout`: if poll_timeout is None or 0, the client polls forever and this error can never fire; it only fires when a positive docling.pool_timeout (or poll_timeout constructor arg) was configured.","triggerScenarios":"Calling convert_from_bytes on AsyncDoclingClient with docling.pool_timeout set (e.g., 600s) while the server keeps returning a pending status — long queue position, heavy load, large multi-hundred-page documents, or a server that is up but stalled. The error message interpolates the configured timeout value.","commonSituations":"Under-provisioned Docling server (CPU-only) processing large PDFs with OCR enabled; many concurrent ingests saturating the server queue; poll_interval set high so checks are sparse near the deadline; timeout configured for sync expectations (30-60s) while async tasks legitimately take minutes.","solutions":["Increase docling.pool_timeout in settings.yaml to a realistic value for your documents (large PDFs with OCR commonly need 600-1800s).","Check Docling server load/queue depth (GET /status/poll/{task_id} shows task_position) — scale the server or reduce concurrent ingests.","Reduce work per task: limit pages via the pages config, disable OCR for digital-native PDFs, or set do_ocr: false.","Verify the server is actually progressing (watch task_position across polls) rather than deadlocked.","Catch TimeoutError at the call site and re-submit or surface a retry-able failure to the user instead of crashing the ingestion batch."],"exampleFix":"# settings.yaml — before\n# docling:\n#   pool_timeout: 60\n\n# after\n# docling:\n#   pool_timeout: 1800\n\n# call-site guard\ntry:\n    result = await client.convert_from_bytes(name, data)\nexcept TimeoutError:\n    logger.warning(\"Docling task for %s timed out; requeueing\", name)\n    raise","handlingStrategy":"retry","validationCode":"# sanity-check timeout vs workload before submitting\npages = file_info.config.get(\"pages\")\nestimated = estimate_seconds(pages or default_page_count)\nif docling_cfg.pool_timeout and docling_cfg.pool_timeout < estimated:\n    logger.warning(\"pool_timeout=%s may be too small for ~%ss of work\", docling_cfg.pool_timeout, estimated)","typeGuard":null,"tryCatchPattern":"import asyncio\n\nfor attempt in range(2):\n    try:\n        return await client.convert_from_bytes(file_name, file_bytes)\n    except TimeoutError:\n        if attempt == 1:\n            raise\n        await asyncio.sleep(10)  # server may still be draining; one re-submit","preventionTips":["Size docling.pool_timeout to your worst-case document (large OCR jobs: 15-30 minutes), not to sync-call expectations.","Monitor task_position in poll responses to distinguish a slow queue from a stuck server.","Remember: leaving pool_timeout unset disables the timeout entirely (infinite polling)."],"tags":["docling","timeout","polling","async"],"backgroundTag":null,"analyzedSha":"4a030776a31a901ad80b1bf4d7faa2c1a367efbb","analyzedAt":"2026-08-15T03:51:26.951Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}