{"record":{"id":"b6df4c799d9d993b","repo":"docling-project/docling","slug":"service-transport-request-failed-after-retries","errorCode":null,"errorMessage":"Service transport request failed after retries.","messagePattern":"Service transport request failed after retries\\.","errorType":"exception","errorClass":"ServiceUnavailableError","httpStatus":null,"severity":"error","filePath":"docling/service_client/client.py","lineNumber":636,"sourceCode":"        return HTTP_RETRY_BACKOFF_BASE_SECONDS * (2**attempt)\n\n    def _transport_retry_delay(\n        self,\n        *,\n        method: str,\n        exc: httpx.HTTPError,\n        attempt: int,\n        max_retries: int,\n    ) -> float | None:\n        method_name = method.upper()\n        if (\n            not isinstance(exc, httpx.TransportError)\n            or method_name not in TRANSPORT_RETRYABLE_HTTP_METHODS\n        ):\n            return None\n        if attempt < max_retries:\n            return self._exponential_backoff_delay(attempt)\n        raise ServiceUnavailableError(\n            \"Service transport request failed after retries.\",\n            detail=str(exc),\n        ) from exc\n\n    def _retry_after_delay_seconds(self, response: httpx.Response) -> float | None:\n        retry_after_header = response.headers.get(\"Retry-After\")\n        if retry_after_header is None:\n            return None\n\n        try:\n            return max(0.0, float(retry_after_header))\n        except ValueError:\n            pass\n\n        try:\n            retry_at = parsedate_to_datetime(retry_after_header)\n        except (TypeError, ValueError, IndexError, OverflowError):\n            return None","sourceCodeStart":618,"sourceCodeEnd":654,"githubUrl":"https://github.com/docling-project/docling/blob/61d76f1ff3f8428065465889f7b4577da7df704c/docling/service_client/client.py#L618-L654","documentation":"Raised as ServiceUnavailableError when an httpx TransportError (connection failure, timeout, reset) occurs on a retryable HTTP method (GET/HEAD/OPTIONS) and the retry budget (max_retries) is exhausted. The original transport exception is chained via 'from exc' and its string is embedded in the detail. It signals the client could not reach the Docling service at the network/transport layer even after exponential backoff retries.","triggerScenarios":"Calling a read/status endpoint such as _poll_task_status or _request_with_retry with method='GET' while the service host is unreachable, DNS fails, the TLS handshake breaks, or the connection times out; the helper retries up to max_retries with _exponential_backoff_delay and, on the final attempt, raises instead of returning a delay.","commonSituations":"Service URL pointing at a stopped/killed local docling-serve instance, corporate proxy or firewall blocking the host, DNS resolution failures in containers, wrong port after the service moved, or transient network outages longer than the retry window.","solutions":["Verify the service is running and reachable: curl the base URL health endpoint (e.g. curl http://localhost:5001/health) from the same machine/container.","Check the base URL configured on DocumentConverter/DoclingServiceClient for typos, wrong port, or wrong scheme.","If a proxy or VPN is required, configure httpx transport/proxy settings or environment variables (HTTP_PROXY/HTTPS_PROXY) in the client environment.","Increase max_retries / backoff settings on the client if the outage is expected to be brief and self-healing.","Wrap calls in a retry loop at the application level with jitter, treating ServiceUnavailableError as transient."],"exampleFix":"// before\nresult = client.convert(\"doc.pdf\")  # raises ServiceUnavailableError when service is down\n\n# after\nfrom docling.service_client.exceptions import ServiceUnavailableError\nimport time\n\nfor attempt in range(3):\n    try:\n        result = client.convert(\"doc.pdf\")\n        break\n    except ServiceUnavailableError:\n        if attempt == 2:\n            raise\n        time.sleep(2 ** attempt)","handlingStrategy":"retry","validationCode":"import socket, urlparse\n\ndef service_reachable(base_url: str, timeout: float = 2.0) -> bool:\n    p = urlparse.urlparse(base_url)\n    try:\n        with socket.create_connection((p.hostname, p.port or 80), timeout=timeout):\n            return True\n    except OSError:\n        return False\n\nassert service_reachable(client_url) before submitting work","typeGuard":null,"tryCatchPattern":"from docling.service_client.exceptions import ServiceUnavailableError\n\ntry:\n    result = client.convert(src)\nexcept ServiceUnavailableError as exc:\n    # transient: detail carries the underlying httpx.TransportError string\n    log.warning(\"transport failure: %s\", exc)\n    raise RetryableError() from exc","preventionTips":["Health-check the service URL before long batch runs.","Run the client in the same network segment as the service when possible.","Configure proxies explicitly instead of relying on ambient environment.","Keep max_retries/backoff configured to ride out short blips."],"tags":["network","retry","service-client","httpx"],"backgroundTag":null,"analyzedSha":"61d76f1ff3f8428065465889f7b4577da7df704c","analyzedAt":"2026-08-14T23:53:18.727Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}