{"record":{"id":"fc5dc0734e953261","repo":"docling-project/docling","slug":"service-request-failed-after-retry-loop","errorCode":null,"errorMessage":"Service request failed after retry loop.","messagePattern":"Service request failed after retry loop\\.","errorType":"exception","errorClass":"ServiceUnavailableError","httpStatus":null,"severity":"error","filePath":"docling/service_client/_async_client.py","lineNumber":709,"sourceCode":"                    method=method_name,\n                    exc=exc,\n                    attempt=attempt,\n                    max_retries=max_retries,\n                )\n                if delay is not None:\n                    await asyncio.sleep(delay)\n                    continue\n                raise ServiceUnavailableError(\n                    \"Service transport request failed.\",\n                    detail=str(exc),\n                ) from exc\n            result, delay = self._check_retry(response, attempt, max_retries)\n            if result is not None:\n                return result\n            if delay > 0:\n                await asyncio.sleep(delay)\n\n        raise ServiceUnavailableError(\"Service request failed after retry loop.\")\n\n    async def _submit_convert_task(\n        self,\n        source: SourceType,\n        options: ConvertDocumentsRequestOptions,\n        target: SubmitTarget,\n        async_client: httpx.AsyncClient,\n        request_headers: dict[str, str] | None = None,\n    ) -> TaskStatusResponse:\n        source = self._normalize_source(source)\n        source_name = self._source_name(source)\n        logger.info(\"Submitting convert task for source=%s\", source_name)\n        if isinstance(source, HttpSourceRequest):\n            request = ConvertDocumentsRequest(\n                options=options,\n                sources=[source],\n                target=target,\n            )","sourceCodeStart":691,"sourceCodeEnd":727,"githubUrl":"https://github.com/docling-project/docling/blob/61d76f1ff3f8428065465889f7b4577da7df704c/docling/service_client/_async_client.py#L691-L727","documentation":"This is the terminal fallback of the async client's retry loop: the request was attempted max_retries+1 times, every attempt either failed transport-level with delays exhausted or returned a retryable HTTP status, and the loop exited without a usable response. ServiceUnavailableError is raised to signal the service did not yield a successful response within the retry budget. Unlike error 350, the last attempt may have been an HTTP-level retry (429/5xx) rather than a transport exception.","triggerScenarios":"Service persistently returning 503/429/502 across all attempts with Retry-After or backoff delays; sustained overload or the service being down for maintenance; retry budget (http_retries) too small for the outage duration.","commonSituations":"Hammering a small docling-serve instance with a large batch until it sheds load; service restarting/deploying mid-job; autoscaling cold start taking longer than the client retry window.","solutions":["Increase http_retries and/or connect/read timeouts on the client so the retry window covers transient outages.","Reduce request concurrency to lower pressure on the service.","Check service health/logs and wait for it to recover before resubmitting.","Implement application-level backoff and resubmit the failed request later."],"exampleFix":"# before\nclient = DocumentConverterClient(base_url=url)  # default retries\nresult = await client.convert_file(f)  # ServiceUnavailableError after retry loop\n\n# after\nclient = DocumentConverterClient(base_url=url, http_retries=8, http_read_timeout=120.0)\nasync with client:\n    try:\n        result = await client.convert_file(f)\n    except ServiceUnavailableError:\n        await asyncio.sleep(60)\n        result = await client.convert_file(f)","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"from docling.service_client.exceptions import ServiceUnavailableError\n\ntry:\n    result = await client.convert_file(f)\nexcept ServiceUnavailableError:\n    await asyncio.sleep(backoff)\n    result = await client.convert_file(f)  # resubmit after service recovers","preventionTips":["Size http_retries to cover expected service restarts/overload windows.","Throttle client concurrency to avoid pushing the service into sustained 5xx.","Watch service health and pause submission during outages."],"tags":["service-client","retry","unavailable","network"],"backgroundTag":null,"analyzedSha":"61d76f1ff3f8428065465889f7b4577da7df704c","analyzedAt":"2026-08-14T23:53:18.727Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}