opendatalab/MinerU · warning · TaskWaitAbortedError

Task manager is shutting down

Error message

Task manager is shutting down

What it means

TaskWaitAbortedError('Task manager is shutting down') raised at fast_api.py:1053 branch inside wait_for_terminal_state: after the wait woke up, the task is gone from self.tasks AND self.is_shutting_down is true. It tells the client the wait was aborted because the API server is in graceful shutdown (tasks and events being torn down), not because the task failed. Whatever partial result existed is not retrievable from this manager instance.

Source

Thrown at mineru/cli/fast_api.py:1048

        pending: set[asyncio.Task[Any]] = set()
        try:
            done, pending = await asyncio.wait(
                {event_wait_task, manager_wait_task},
                return_when=asyncio.FIRST_COMPLETED,
            )
        finally:
            for waiter in pending:
                waiter.cancel()
            if pending:
                await asyncio.gather(*pending, return_exceptions=True)
            for waiter in done:
                with suppress(asyncio.CancelledError):
                    waiter.result()

        task = self.tasks.get(task_id)
        if task is None:
            if self.is_shutting_down:
                raise TaskWaitAbortedError("Task manager is shutting down")
            raise TaskWaitAbortedError("Task was removed before completion")
        if is_task_terminal(task.status):
            return task
        if self.is_shutting_down:
            raise TaskWaitAbortedError("Task manager is shutting down")
        raise TaskWaitAbortedError(
            self.last_worker_error or "Task manager became unavailable while waiting"
        )

    def get_stats(self) -> dict[str, int]:
        stats = {
            TASK_PENDING: 0,
            TASK_PROCESSING: 0,
            TASK_COMPLETED: 0,
            TASK_FAILED: 0,
        }
        for task in self.tasks.values():
            if task.status in stats:

View on GitHub (pinned to 4fe4bde114)

Solutions

  1. Treat as retryable: resubmit the parse job to a healthy instance once the server is back.
  2. Put the API behind a process manager that drains in-flight tasks before shutdown (grace period longer than worst parse time).
  3. Client-side, distinguish this from a real failure: catch TaskWaitAbortedError and re-enqueue instead of marking the document as failed.
  4. For very long documents, prefer the async endpoints over the synchronous /file_parse so a shutdown does not drop the whole HTTP wait.

Example fix

# before
result = await client.get(f'{base}/file_parse-result/{task_id}')  # wait dies on server shutdown

# after
try:
    result = await wait_terminal(task_id)
except TaskWaitAbortedError as exc:
    if 'shutting down' in str(exc):
        await asyncio.sleep(RESTART_BACKOFF)
        task_id = await submit(files)  # resubmit after server returns
        result = await wait_terminal(task_id)
Defensive patterns

Strategy: retry

Try / catch

try:
    task = await manager.wait_for_terminal_state(task_id)
except TaskWaitAbortedError as exc:
    if 'shutting down' in str(exc):
        await wait_server_healthy(base_url)
        task_id = await resubmit(files)
        task = await manager.wait_for_terminal_state(task_id)
    else:
        raise

Prevention

When it happens

Trigger: Long-polling a task while the server receives SIGTERM/SIGINT or a lifespan shutdown; container orchestrator rolling-restart during an active parse; health-check failure triggering process teardown mid-wait.

Common situations: Kubernetes/container restarts during long VLM parses; CI jobs that ctrl-C the server while a client waits; deploying a new version while requests are in flight.

Related errors


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