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

  1. Increase the wait budget: construct the poller/client with a larger max_wait_time (or the SDK's timeout option) proportional to document size.
  2. Catch PollTimeoutError and resume by polling the same job_id again rather than resubmitting the file.
  3. Check job status once via the API (get_job_status) to see whether it eventually completes.
  4. 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

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

Related errors


AI-assisted analysis of PaddlePaddle/PaddleOCR@2661c7c0ef (2026-08-14). Data as JSON: /api/errors/14797f5ab327ef29. Report an issue: GitHub.