docling-project/docling · error · ServiceUnavailableError

WebSocket status stream is unavailable.

Error message

WebSocket status stream is unavailable.

What it means

ServiceUnavailableError raised by WebSocketWatcher._iter_ws_updates when the WS connection drops (ConnectionClosedError/OSError) and either WS_MAX_RECONNECT_ATTEMPTS is exhausted or the overall deadline passed before a successful reconnect. Each failure logs a warning and backs off exponentially (WS_RECONNECT_BACKOFF_BASE_SECONDS * 2**attempt) before retrying.

Source

Thrown at docling/service_client/watchers.py:207

            raise TaskTimeoutError(
                f"Timed out waiting for task {task_id} to emit status updates."
            )
        return final_status

    def _iter_ws_updates(
        self, task_id: str, timeout: float
    ) -> Iterator[TaskStatusResponse]:
        ws_url = self._ws_url_for_task(task_id)
        deadline = time.monotonic() + timeout

        for attempt in range(WS_MAX_RECONNECT_ATTEMPTS + 1):
            try:
                yield from self._iter_ws_connection(ws_url, task_id, deadline, timeout)
                return
            except (ConnectionClosedError, OSError) as exc:
                remaining = deadline - time.monotonic()
                if attempt >= WS_MAX_RECONNECT_ATTEMPTS or remaining <= 0:
                    raise ServiceUnavailableError(
                        "WebSocket status stream is unavailable.", detail=str(exc)
                    ) from exc
                delay = min(WS_RECONNECT_BACKOFF_BASE_SECONDS * (2**attempt), remaining)
                _logger.warning(
                    "WebSocket connection dropped for task %s: %s — reconnecting in %.1fs",
                    task_id,
                    exc,
                    delay,
                )
                time.sleep(delay)
            except (TaskTimeoutError, TaskNotFoundError, ServiceUnavailableError):
                raise
            except Exception as exc:
                raise ServiceUnavailableError(
                    "WebSocket status stream is unavailable.", detail=str(exc)
                ) from exc

    def _iter_ws_connection(

View on GitHub (pinned to 61d76f1ff3)

Solutions

  1. Catch ServiceUnavailableError and re-attach with a fresh iter_updates/wait_for_terminal call — the task itself may still be alive server-side
  2. Raise websocket idle timeouts on the proxy/ingress (e.g. proxy_read_timeout for nginx)
  3. Increase the overall wait timeout so the reconnect backoff fits inside the deadline

Example fix

try:
    status = watcher.wait_for_terminal(task_id, timeout=1800.0)
except ServiceUnavailableError:
    # WS budget exhausted; task may still run — re-attach
    status = watcher.wait_for_terminal(task_id, timeout=1800.0)
Defensive patterns

Strategy: fallback

Type guard

def is_service_unavailable(exc: BaseException) -> bool:
    return isinstance(exc, ServiceUnavailableError)

Try / catch

from docling.service_client.exceptions import ServiceUnavailableError

try:
    status = watcher.wait_for_terminal(task_id, timeout=timeout_s)
except ServiceUnavailableError as exc:
    if 'WebSocket status stream' in str(exc):
        status = poll_fallback.wait_for_terminal(task_id, timeout=timeout_s)

Prevention

When it happens

Trigger: Watching task status over WS /v1/status/ws/{task_id} through a proxy/LB that kills idle or long-lived connections faster than the reconnect budget covers; service restarting; network partition during a long conversion.

Common situations: Corporate proxies or ingress controllers with short websocket idle timeouts; docling-serve pod evictions mid-task; flaky Wi-Fi/VPN links during lengthy waits.

Related errors


AI-assisted analysis of docling-project/docling@61d76f1ff3 (2026-08-14). Data as JSON: /api/errors/66dd7d565c0a7b90. Report an issue: GitHub.