{"record":{"id":"fb70c7ce051c7df6","repo":"docling-project/docling","slug":"too-many-redirects-while-downloading-artifact","errorCode":null,"errorMessage":"Too many redirects while downloading artifact.","messagePattern":"Too many redirects while downloading artifact\\.","errorType":"exception","errorClass":"ArtifactDownloadError","httpStatus":null,"severity":"error","filePath":"docling/service_client/client.py","lineNumber":1910,"sourceCode":"                for _ in range(MAX_ARTIFACT_DOWNLOAD_REDIRECTS + 1):\n                    self._validate_artifact_url(url)\n                    with client.stream(\"GET\", url) as response:\n                        if response.is_redirect:\n                            url = self._next_redirect_url(url, response)\n                            continue\n                        if response.status_code != 200:\n                            raise ArtifactDownloadError(\n                                \"Artifact download failed with HTTP \"\n                                f\"{response.status_code}.\"\n                            )\n                        chunks: list[bytes] = []\n                        total = 0\n                        for chunk in response.iter_bytes():\n                            total += len(chunk)\n                            self._check_artifact_size(total)\n                            chunks.append(chunk)\n                        return b\"\".join(chunks)\n                raise ArtifactDownloadError(\n                    \"Too many redirects while downloading artifact.\"\n                )\n        except httpx.HTTPError as exc:\n            raise ArtifactDownloadError(f\"Artifact download failed: {exc}\") from exc\n\n    async def _download_artifact_bytes_async(self, uri: str) -> bytes:\n        timeout = httpx.Timeout(self._artifact_download_timeout)\n        try:\n            async with httpx.AsyncClient(\n                timeout=timeout, follow_redirects=False\n            ) as client:\n                url = uri\n                for _ in range(MAX_ARTIFACT_DOWNLOAD_REDIRECTS + 1):\n                    self._validate_artifact_url(url)\n                    async with client.stream(\"GET\", url) as response:\n                        if response.is_redirect:\n                            url = self._next_redirect_url(url, response)\n                            continue","sourceCodeStart":1892,"sourceCodeEnd":1928,"githubUrl":"https://github.com/docling-project/docling/blob/61d76f1ff3f8428065465889f7b4577da7df704c/docling/service_client/client.py#L1892-L1928","documentation":"Raised as ArtifactDownloadError by the synchronous artifact download when the redirect chain exceeds MAX_ARTIFACT_DOWNLOAD_REDIRECTS (5). The client deliberately disables httpx auto-redirects and hops manually so every intermediate URL passes the SSRF validation; after 6 hops without a 200 it gives up.","triggerScenarios":"An artifact URL that bounces through more than 5 redirects (auth-less CDN chains, misconfigured object-store mirrors, or a redirect loop between two hosts).","commonSituations":"Object-store fronted by multiple reverse proxies each adding a redirect; an intentional or accidental redirect loop; a compromised service trying to bounce the client around.","solutions":["Fetch the artifact URL manually with curl -IL to inspect the redirect chain","Fix the artifact host configuration to serve the object directly (fewer proxy hops)","If the chain is legitimate and longer than 5, this limit is a constant in the client — request an upstream change rather than working around the SSRF guard"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"import requests\nr = requests.head(artifact_url, allow_redirects=False)\nhops = 0\nwhile r.is_redirect and hops < 10:\n    artifact_url = r.headers['Location']\n    r = requests.head(artifact_url, allow_redirects=False)\n    hops += 1\nassert hops <= 5, f'{hops} redirect hops exceeds client cap'","typeGuard":"def is_artifact_download_error(exc: BaseException) -> bool:\n    return isinstance(exc, ArtifactDownloadError)","tryCatchPattern":"try:\n    results = list(client.convert_all(sources))\nexcept ArtifactDownloadError as exc:\n    if 'Too many redirects' in str(exc):\n        alert_ops('artifact host redirect chain too long')","preventionTips":["Pre-flight the artifact URL redirect chain when deploying new object-store front-ends","Keep artifact serving behind at most a couple of proxy layers"],"tags":["network","redirects","artifact-download","security"],"backgroundTag":null,"analyzedSha":"61d76f1ff3f8428065465889f7b4577da7df704c","analyzedAt":"2026-08-14T23:53:18.727Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}