docling-project/docling · error · ServiceUnavailableError

Service returned HTTP {response.status_code} after retries.

Error message

Service returned HTTP {response.status_code} after retries.

What it means

The mirror of error 358 for responses that DO carry a Retry-After header: the sync client honors the header's delay and retries, but once attempt >= max_retries it stops and raises ServiceUnavailableError stating the service returned that HTTP status after all retries. Typical for 429 rate limiting or 503 load shedding where the server keeps asking the client to come back later.

Source

Thrown at docling/service_client/client.py:611

            return None, self._exponential_backoff_delay(attempt)
        raise ServiceUnavailableError(
            error_message,
            status_code=response.status_code,
            detail=self._http_error_detail(response),
        )

    def _retry_with_retry_after_header(
        self,
        response: httpx.Response,
        attempt: int,
        max_retries: int,
    ) -> tuple[httpx.Response | None, float]:
        retry_after_delay = self._retry_after_delay_seconds(response)
        if retry_after_delay is None:
            return response, 0.0
        if attempt < max_retries:
            return None, retry_after_delay
        raise ServiceUnavailableError(
            f"Service returned HTTP {response.status_code} after retries.",
            status_code=response.status_code,
            detail=self._http_error_detail(response),
        )

    def _exponential_backoff_delay(self, attempt: int) -> float:
        return HTTP_RETRY_BACKOFF_BASE_SECONDS * (2**attempt)

    def _transport_retry_delay(
        self,
        *,
        method: str,
        exc: httpx.HTTPError,
        attempt: int,
        max_retries: int,
    ) -> float | None:
        method_name = method.upper()
        if (

View on GitHub (pinned to 61d76f1ff3)

Solutions

  1. Reduce request concurrency/rate on the client side to stay under limits.
  2. Raise http_retries so the client survives longer throttle windows honoring Retry-After.
  3. Catch ServiceUnavailableError, wait, and resubmit the remaining work later.
  4. Scale the service or negotiate higher limits if throttling is steady-state.

Example fix

# before
client = DocumentConverterClient(url, max_concurrency=64)
results = [client.convert_file(f) for f in many_files]  # 429s exhaust retries

# after
client = DocumentConverterClient(url, max_concurrency=4, http_retries=8)
from docling.service_client.exceptions import ServiceUnavailableError
import time
for f in many_files:
    for attempt in range(3):
        try:
            results.append(client.convert_file(f)); break
        except ServiceUnavailableError:
            time.sleep(30)
Defensive patterns

Strategy: retry

Try / catch

from docling.service_client.exceptions import ServiceUnavailableError
import time

for attempt in range(5):
    try:
        result = client.convert_file(f)
        break
    except ServiceUnavailableError as e:
        if e.status_code not in (429, 503):
            raise
        time.sleep(min(60, 5 * 2 ** attempt))

Prevention

When it happens

Trigger: Sustained 429 rate limiting with Retry-After exceeding the retry budget; service shedding load with 503+Retry-After during peak; too many concurrent client requests against a small docling-serve instance.

Common situations: Batch conversion tripping rate limits; shared service instances throttling per-client; retry budget (default few attempts) shorter than the throttle window.

Related errors


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