opendatalab/MinerU · warning · TaskWaitAbortedError
Task wait handle is unavailable
Error message
Task wait handle is unavailable
What it means
TaskWaitAbortedError('Task wait handle is unavailable') raised in wait_for_terminal_state when the task record exists and is non-terminal, but there is no asyncio.Event for it in self.task_events. The event map and task map are meant to stay in sync, so this indicates an internal inconsistency: the event was already consumed/removed (e.g. by cleanup or shutdown paths) while the task entry lingers. End users cannot cause it directly; it is a server-side lifecycle bug or a race during manager teardown.
Source
Thrown at mineru/cli/fast_api.py:1025
self,
task: AsyncParseTask,
request: Request,
) -> dict[str, Any]:
return task.to_status_payload(
request,
queued_ahead=self.get_queued_ahead(task.task_id),
)
async def wait_for_terminal_state(self, task_id: str) -> AsyncParseTask:
task = self.tasks.get(task_id)
if task is None:
raise TaskWaitAbortedError("Task not found")
if is_task_terminal(task.status):
return task
task_event = self.task_events.get(task_id)
if task_event is None:
raise TaskWaitAbortedError("Task wait handle is unavailable")
event_wait_task = asyncio.create_task(task_event.wait())
manager_wait_task = asyncio.create_task(self.manager_wakeup.wait())
done: set[asyncio.Task[Any]] = set()
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()View on GitHub (pinned to 4fe4bde114)
Solutions
- Retry the status request once: the very next lookup usually either finds a terminal task (returns before the event is needed) or reports Task not found, both handled.
- Resubmit the job if the retry keeps failing — the manager state for that task is corrupted.
- Upgrade mineru: this is an internal invariant violation worth reporting upstream with logs of the cleanup timeline.
- Avoid issuing long-poll waits during planned shutdown windows.
Example fix
# before
status = await wait_for_terminal_state(task_id) # may raise TaskWaitAbortedError
# after
try:
status = await wait_for_terminal_state(task_id)
except TaskWaitAbortedError:
task = task_manager.get(task_id) # fall back to one-shot lookup
status = task if task else resubmit() Defensive patterns
Strategy: retry
Try / catch
try:
task = await manager.wait_for_terminal_state(task_id)
except TaskWaitAbortedError:
snapshot = manager.get(task_id)
if snapshot is None or is_task_terminal(snapshot.status):
return snapshot # consistent state reached after one retry
raise Prevention
- Avoid long-poll waits while the server is shutting down or cleanup sweeps run.
- Report recurring occurrences upstream — this is an internal invariant break, not a usage error.
When it happens
Trigger: A long-poll request in flight while the background cleanup or shutdown code removes the task's event but not (yet) the task; calling wait_for_terminal_state concurrently with manager shutdown; any code path that deletes from task_events independently of tasks.
Common situations: Hitting the status endpoint exactly as retention cleanup runs; server shutdown while clients are long-polling; upgrading between mineru versions where the cleanup logic changed how events are removed.
Related errors
- Failed to load file {upload.original_name}: {exc}
- Task manager is shutting down
- Unsupported file type: {file_suffix}
- Task manager is not initialized
- Unknown process_mode: {process_mode}
AI-assisted analysis of opendatalab/MinerU@4fe4bde114 (2026-08-14).
Data as JSON: /api/errors/8692b57e0eaf2a3c.
Report an issue: GitHub.