{"record":{"id":"5a3f58d0f7117939","repo":"opendatalab/MinerU","slug":"task-manager-is-shutting-down","errorCode":null,"errorMessage":"Task manager is shutting down","messagePattern":"Task manager is shutting down","errorType":"exception","errorClass":"TaskWaitAbortedError","httpStatus":503,"severity":"warning","filePath":"mineru/cli/fast_api.py","lineNumber":1048,"sourceCode":"        pending: set[asyncio.Task[Any]] = set()\n        try:\n            done, pending = await asyncio.wait(\n                {event_wait_task, manager_wait_task},\n                return_when=asyncio.FIRST_COMPLETED,\n            )\n        finally:\n            for waiter in pending:\n                waiter.cancel()\n            if pending:\n                await asyncio.gather(*pending, return_exceptions=True)\n            for waiter in done:\n                with suppress(asyncio.CancelledError):\n                    waiter.result()\n\n        task = self.tasks.get(task_id)\n        if task is None:\n            if self.is_shutting_down:\n                raise TaskWaitAbortedError(\"Task manager is shutting down\")\n            raise TaskWaitAbortedError(\"Task was removed before completion\")\n        if is_task_terminal(task.status):\n            return task\n        if self.is_shutting_down:\n            raise TaskWaitAbortedError(\"Task manager is shutting down\")\n        raise TaskWaitAbortedError(\n            self.last_worker_error or \"Task manager became unavailable while waiting\"\n        )\n\n    def get_stats(self) -> dict[str, int]:\n        stats = {\n            TASK_PENDING: 0,\n            TASK_PROCESSING: 0,\n            TASK_COMPLETED: 0,\n            TASK_FAILED: 0,\n        }\n        for task in self.tasks.values():\n            if task.status in stats:","sourceCodeStart":1030,"sourceCodeEnd":1066,"githubUrl":"https://github.com/opendatalab/MinerU/blob/4fe4bde114a23ee5dd637eae99b767f4669bf58c/mineru/cli/fast_api.py#L1030-L1066","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Treat as retryable: resubmit the parse job to a healthy instance once the server is back.","Put the API behind a process manager that drains in-flight tasks before shutdown (grace period longer than worst parse time).","Client-side, distinguish this from a real failure: catch TaskWaitAbortedError and re-enqueue instead of marking the document as failed.","For very long documents, prefer the async endpoints over the synchronous /file_parse so a shutdown does not drop the whole HTTP wait."],"exampleFix":"# before\nresult = await client.get(f'{base}/file_parse-result/{task_id}')  # wait dies on server shutdown\n\n# after\ntry:\n    result = await wait_terminal(task_id)\nexcept TaskWaitAbortedError as exc:\n    if 'shutting down' in str(exc):\n        await asyncio.sleep(RESTART_BACKOFF)\n        task_id = await submit(files)  # resubmit after server returns\n        result = await wait_terminal(task_id)","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"try:\n    task = await manager.wait_for_terminal_state(task_id)\nexcept TaskWaitAbortedError as exc:\n    if 'shutting down' in str(exc):\n        await wait_server_healthy(base_url)\n        task_id = await resubmit(files)\n        task = await manager.wait_for_terminal_state(task_id)\n    else:\n        raise","preventionTips":["Graceful-shutdown drain periods must exceed your longest parse time.","Prefer async task endpoints for long documents so server restarts do not kill the HTTP wait.","Teach clients that TaskWaitAbortedError during deploys means resubmit, not document failure."],"tags":["mineru","fastapi","shutdown","task-management","retryable"],"backgroundTag":null,"analyzedSha":"4fe4bde114a23ee5dd637eae99b767f4669bf58c","analyzedAt":"2026-08-14T21:29:18.456Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}