{"record":{"id":"67de69f9f886a636","repo":"PaddlePaddle/PaddleOCR","slug":"timed-out-after-elapsed-1f-s-waiting-for-job-jo-67de69","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":"error","filePath":"paddleocr/_api_client/_poller.py","lineNumber":67,"sourceCode":"        multiplier: float = DEFAULT_MULTIPLIER,\n        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    def poll_until_done(self, job_id: str) -> Any:\n        interval = self._initial_interval\n        start = time.monotonic()\n        deadline = start + self._max_wait_time\n\n        while True:\n            now = time.monotonic()\n            if now >= deadline:\n                raise PollTimeoutError(job_id, now - start)\n\n            data = 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 = 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 - time.monotonic()\n            if remaining <= 0:\n                raise PollTimeoutError(job_id, time.monotonic() - start)\n\n            time.sleep(min(interval, remaining))","sourceCodeStart":49,"sourceCodeEnd":85,"githubUrl":"https://github.com/PaddlePaddle/PaddleOCR/blob/2661c7c0ef5c613e8f93c6e93b2e052399f0f854/paddleocr/_api_client/_poller.py#L49-L85","documentation":"PollTimeoutError raised by the poller's top-of-loop deadline check: the asynchronous OCR/PP-StructureV3 job did not reach 'done' or 'failed' within max_wait_time seconds. The poller uses exponential backoff (initial_interval * multiplier capped at max_interval) and gives up once time.monotonic() passes the deadline computed at poll start.","triggerScenarios":"Calling an async submit-and-poll API (e.g. submit a large PDF for OCR) with a max_wait_time shorter than the server's actual processing time; the job stays in a pending/running state past the configured budget. The check fires on the first loop iteration whose time.monotonic() >= deadline.","commonSituations":"Large multi-page documents on a slow or shared PaddleOCR backend; server queue congestion; max_wait_time left at a default too small for batch workloads; network latency inflating each get_job_status round trip so fewer polls fit in the budget.","solutions":["Retry the poll — the job usually still completes server-side; re-poll with poller.get_status(job_id) or a fresh poll_until_done call with a larger budget","Increase max_wait_time (and optionally max_interval) when constructing the poller for large documents or batches","Check job status manually once after the timeout to see whether it eventually finishes, then fetch the result URL directly","If jobs never finish, investigate server capacity / job queue state rather than the client timeout"],"exampleFix":"# before\npoller = JobPoller(http, max_wait_time=300)  # times out on big PDFs\njsonl, raw = poller.poll_until_done(job_id)\n\n# after\npoller = JobPoller(http, max_wait_time=1800, max_interval=30)\njsonl, raw = poller.poll_until_done(job_id)","handlingStrategy":"retry","validationCode":"# no pre-call validation possible; estimate budget from input size\npages = count_pages(pdf_path)\nmax_wait = max(300, pages * 30)  # rough seconds-per-page budget","typeGuard":null,"tryCatchPattern":"from paddleocr._api_client.errors import PollTimeoutError\ntry:\n    jsonl, raw = poller.poll_until_done(job_id)\nexcept PollTimeoutError:\n    status = poller.get_status(job_id)  # job may still finish\n    raise","preventionTips":["Size max_wait_time from document size, not a constant","For large batches, use get_batch_status instead of per-job polling budgets","Persist the job_id so a timed-out job can be re-polled instead of resubmitted"],"tags":["timeout","polling","async-jobs"],"backgroundTag":null,"analyzedSha":"2661c7c0ef5c613e8f93c6e93b2e052399f0f854","analyzedAt":"2026-08-14T20:17:30.180Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}