{"record":{"id":"14797f5ab327ef29","repo":"PaddlePaddle/PaddleOCR","slug":"timed-out-after-elapsed-1f-s-waiting-for-job-jo","errorCode":null,"errorMessage":"Timed out after {elapsed:.1f}s waiting for job {job_id}","messagePattern":"Timed out after (.+?)s waiting for job (.+?)","errorType":"exception","errorClass":"PollTimeoutError","httpStatus":null,"severity":"warning","filePath":"paddleocr/_api_client/_async_poller.py","lineNumber":60,"sourceCode":"        max_interval: float = DEFAULT_MAX_INTERVAL,\n        max_wait_time: float = DEFAULT_MAX_WAIT_TIME,\n    ):\n        self._http = http_client\n        self._initial_interval = initial_interval\n        self._multiplier = multiplier\n        self._max_interval = max_interval\n        self._max_wait_time = max_wait_time\n\n    async def poll_until_done(self, job_id: str) -> Any:\n        interval = self._initial_interval\n        loop = asyncio.get_running_loop()\n        start = loop.time()\n        deadline = start + self._max_wait_time\n\n        while True:\n            now = loop.time()\n            if now >= deadline:\n                raise PollTimeoutError(job_id, now - start)\n\n            data = await self._http.get_job_status(job_id)\n            state = validate_state(data)\n\n            if state == \"done\":\n                json_url = validate_result_json_url(data)\n                jsonl_data = await self._http.fetch_jsonl(json_url)\n                return jsonl_data, data\n\n            if state == \"failed\":\n                error_msg = data.get(\"errorMsg\", \"Unknown error\")\n                raise JobFailedError(job_id, error_msg)\n\n            remaining = deadline - loop.time()\n            if remaining <= 0:\n                raise PollTimeoutError(job_id, loop.time() - start)\n            await asyncio.sleep(min(interval, remaining))\n            interval = min(interval * self._multiplier, self._max_interval)","sourceCodeStart":42,"sourceCodeEnd":78,"githubUrl":"https://github.com/PaddlePaddle/PaddleOCR/blob/2661c7c0ef5c613e8f93c6e93b2e052399f0f854/paddleocr/_api_client/_async_poller.py#L42-L78","documentation":"PollTimeoutError from AsyncPoller.poll_until_done: the loop checks the event-loop clock against a deadline of start + max_wait_time (default 600s) before each status poll; once elapsed it raises with the job id and elapsed seconds. The job may still be running server-side — the timeout is client-side patience, not a job failure.","triggerScenarios":"Submitting a large multi-page PDF whose parsing legitimately exceeds the wait budget; a queue backlog on the service; tight user-configured max_wait_time; interval backoff capping at max_interval so late status changes are noticed slowly.","commonSituations":"Batch document workloads in CI with fixed timeouts; slow qianfan/appstore queues at peak hours; first-time users keeping the 10-minute default for big files.","solutions":["Increase the wait budget: construct the poller/client with a larger max_wait_time (or the SDK's timeout option) proportional to document size.","Catch PollTimeoutError and resume by polling the same job_id again rather than resubmitting the file.","Check job status once via the API (get_job_status) to see whether it eventually completes.","For big documents, split them into smaller batches or submit off-peak."],"exampleFix":"// before\npoller = AsyncPoller(http, max_wait_time=600.0)\n\n// after\npoller = AsyncPoller(http, max_wait_time=3600.0)\n\n# and resume instead of resubmitting:\ntry:\n    data = await poller.poll_until_done(job_id)\nexcept PollTimeoutError:\n    data = await poller.poll_until_done(job_id)  # job may still finish server-side","handlingStrategy":"retry","validationCode":"def wait_budget_sufficient(page_count: int, seconds_per_page: float = 2.0, overhead: float = 60.0) -> float:\n    return overhead + seconds_per_page * page_count  # pass as max_wait_time","typeGuard":null,"tryCatchPattern":"from paddleocr._api_client.errors import PollTimeoutError\n\ntry:\n    result = await poller.poll_until_done(job_id)\nexcept PollTimeoutError:\n    # job may still complete server-side; resume polling instead of resubmitting\n    result = await poller.poll_until_done(job_id)\nexcept JobFailedError:\n    raise  # genuine failure — do not retry","preventionTips":["Size max_wait_time to document size (pages × per-page latency + margin).","Catch PollTimeoutError separately from JobFailedError: one means 'keep waiting', the other means 'give up'.","Persist job_id at submission so a timeout can be resumed in a new process.","Avoid unbounded retries — cap total patience and surface the job id for manual status checks."],"tags":["python","asyncio","timeout","polling","api-client"],"backgroundTag":null,"analyzedSha":"2661c7c0ef5c613e8f93c6e93b2e052399f0f854","analyzedAt":"2026-08-14T20:17:30.180Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}