opendatalab/MinerU · error · RuntimeError

Timed out waiting for local worker {server_id} to become hea

Error message

Timed out waiting for local worker {server_id} to become healthy

What it means

RuntimeError raised by LocalWorker.wait_until_ready in router.py: the worker subprocess is still alive but its /health endpoint has not returned 200 within LOCAL_API_STARTUP_TIMEOUT_SECONDS. The message includes the last observed error (non-200 detail or httpx transport error). Typical when startup work — downloading model weights, warming the VLM, loading pipelines — takes longer than the fixed timeout.

Source

Thrown at mineru/cli/router.py:475

        assert self.base_url is not None
        deadline = asyncio.get_running_loop().time() + timeout_seconds
        last_error: str | None = None
        while asyncio.get_running_loop().time() < deadline:
            if self.process is not None and self.process.poll() is not None:
                raise RuntimeError(f"Local worker {self.server_id} exited before becoming healthy")
            try:
                response = await client.get(f"{self.base_url}{HEALTH_ENDPOINT}")
                if response.status_code == 200:
                    return
                last_error = response_detail(response)
            except httpx.HTTPError as exc:
                last_error = str(exc)
            await asyncio.sleep(TASK_STATUS_POLL_INTERVAL_SECONDS)

        message = f"Timed out waiting for local worker {self.server_id} to become healthy"
        if last_error:
            message = f"{message}: {last_error}"
        raise RuntimeError(message)

    async def restart(self, client: httpx.AsyncClient) -> None:
        self.stop()
        await self.start(client)

    def stop(self) -> None:
        process = self.process
        process_group_id = self.process_group_id
        self.process = None
        self.process_group_id = None
        try:
            if process is not None or process_group_id is not None:
                stop_managed_process(
                    process,
                    process_group_id=process_group_id,
                    shutdown_timeout_seconds=5,
                    use_stdin_shutdown_watcher=False,
                )

View on GitHub (pinned to 4fe4bde114)

Solutions

  1. Pre-download/warm the models once (run the mineru CLI on a sample file) so worker startup is fast afterwards.
  2. Increase LOCAL_API_STARTUP_TIMEOUT_SECONDS (or the corresponding constant/env in your version) if your hardware legitimately needs longer.
  3. Read the trailing ': {last_error}' in the message — connection errors mean not-yet-listening; 5xx means the app is up but unhealthy (fix that).
  4. Put model caches on fast local storage and keep them across container restarts (volume-mount ~/.cache or MINERU_MODEL_PATH).

Example fix

# before
# RuntimeError: Timed out waiting for local worker 1 to become healthy: [Errno 111] Connection refused
# (first run, downloading models)

# after
# 1) warm cache once:
#    mineru -p sample.pdf -o /tmp/warmup
# 2) persist the model cache in Docker:
#    docker run -v mineru-cache:/root/.cache ... 
Defensive patterns

Strategy: retry

Validate before calling

import httpx, time

def worker_will_be_ready(base_url: str, budget_s: float) -> bool:
    deadline = time.time() + budget_s
    while time.time() < deadline:
        try:
            if httpx.get(f'{base_url}/health', timeout=2).status_code == 200:
                return True
        except httpx.HTTPError:
            pass
        time.sleep(1)
    return False

Try / catch

for attempt in range(3):
    try:
        await worker.wait_until_ready(client)
        break
    except RuntimeError as exc:
        if 'Timed out' not in str(exc) or attempt == 2:
            raise
        await asyncio.sleep(10)  # cold model cache warms on later attempts

Prevention

When it happens

Trigger: First run downloading multi-GB model files over a slow link; cold model cache on a new machine/container; slow disk (network volume) inflating load time; health checks failing with connection refused because binding is delayed; CPU-only machines loading VLM backends very slowly.

Common situations: See trigger scenarios.

Understand the failure class

Related errors


AI-assisted analysis of opendatalab/MinerU@4fe4bde114 (2026-08-14). Data as JSON: /api/errors/00ff212c25f1fd57. Report an issue: GitHub.