PaddlePaddle/PaddleOCR · warning · PollTimeoutError
Timed out after {elapsed:.1f}s waiting for job {job_id}
Error message
Timed out after {elapsed:.1f}s waiting for job {job_id} What it means
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.
Source
Thrown at paddleocr/_api_client/_async_poller.py:60
max_interval: float = DEFAULT_MAX_INTERVAL,
max_wait_time: float = DEFAULT_MAX_WAIT_TIME,
):
self._http = http_client
self._initial_interval = initial_interval
self._multiplier = multiplier
self._max_interval = max_interval
self._max_wait_time = max_wait_time
async def poll_until_done(self, job_id: str) -> Any:
interval = self._initial_interval
loop = asyncio.get_running_loop()
start = loop.time()
deadline = start + self._max_wait_time
while True:
now = loop.time()
if now >= deadline:
raise PollTimeoutError(job_id, now - start)
data = await self._http.get_job_status(job_id)
state = validate_state(data)
if state == "done":
json_url = validate_result_json_url(data)
jsonl_data = await self._http.fetch_jsonl(json_url)
return jsonl_data, data
if state == "failed":
error_msg = data.get("errorMsg", "Unknown error")
raise JobFailedError(job_id, error_msg)
remaining = deadline - loop.time()
if remaining <= 0:
raise PollTimeoutError(job_id, loop.time() - start)
await asyncio.sleep(min(interval, remaining))
interval = min(interval * self._multiplier, self._max_interval)View on GitHub (pinned to 2661c7c0ef)
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.
Example fix
// before
poller = AsyncPoller(http, max_wait_time=600.0)
// after
poller = AsyncPoller(http, max_wait_time=3600.0)
# and resume instead of resubmitting:
try:
data = await poller.poll_until_done(job_id)
except PollTimeoutError:
data = await poller.poll_until_done(job_id) # job may still finish server-side Defensive patterns
Strategy: retry
Validate before calling
def wait_budget_sufficient(page_count: int, seconds_per_page: float = 2.0, overhead: float = 60.0) -> float:
return overhead + seconds_per_page * page_count # pass as max_wait_time Try / catch
from paddleocr._api_client.errors import PollTimeoutError
try:
result = await poller.poll_until_done(job_id)
except PollTimeoutError:
# job may still complete server-side; resume polling instead of resubmitting
result = await poller.poll_until_done(job_id)
except JobFailedError:
raise # genuine failure — do not retry Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- {file_path}
- Malformed JSONL result payload: {e}
- Response body is not valid JSON: {e}
- Document parsing result item is missing result.layoutParsing
- Job ${job.jobId} is a ${job.task} job, not a ${expectedTask}
AI-assisted analysis of PaddlePaddle/PaddleOCR@2661c7c0ef (2026-08-14).
Data as JSON: /api/errors/14797f5ab327ef29.
Report an issue: GitHub.