docling-project/docling · error · ArtifactDownloadError

Artifact download failed with HTTP {response.status_code}.

Error message

Artifact download failed with HTTP {response.status_code}.

What it means

Raised as ArtifactDownloadError by the synchronous _download_artifact_bytes when the artifact server answers with a non-200, non-redirect status (403 expired presigned URL, 404 deleted object, 500, etc.). Each redirect hop is re-validated and followed manually; any final status other than 200 fails. Normally converted into a FAILURE ConversionResult by the materialization path.

Source

Thrown at docling/service_client/client.py:1899

        """Download an external presigned artifact safely (sync).

        Uses a dedicated client so the service ``X-Api-Key`` header is never sent
        to the (external) artifact storage endpoint, validates every hop against
        the SSRF guard, and enforces a streamed size cap. Redirects are followed
        manually so each target can be re-validated.
        """
        timeout = httpx.Timeout(self._artifact_download_timeout)
        try:
            with httpx.Client(timeout=timeout, follow_redirects=False) as client:
                url = uri
                for _ in range(MAX_ARTIFACT_DOWNLOAD_REDIRECTS + 1):
                    self._validate_artifact_url(url)
                    with client.stream("GET", url) as response:
                        if response.is_redirect:
                            url = self._next_redirect_url(url, response)
                            continue
                        if response.status_code != 200:
                            raise ArtifactDownloadError(
                                "Artifact download failed with HTTP "
                                f"{response.status_code}."
                            )
                        chunks: list[bytes] = []
                        total = 0
                        for chunk in response.iter_bytes():
                            total += len(chunk)
                            self._check_artifact_size(total)
                            chunks.append(chunk)
                        return b"".join(chunks)
                raise ArtifactDownloadError(
                    "Too many redirects while downloading artifact."
                )
        except httpx.HTTPError as exc:
            raise ArtifactDownloadError(f"Artifact download failed: {exc}") from exc

    async def _download_artifact_bytes_async(self, uri: str) -> bytes:
        timeout = httpx.Timeout(self._artifact_download_timeout)

View on GitHub (pinned to 61d76f1ff3)

Solutions

  1. Retry the conversion — expired presigned URLs are transient and a new task gets a fresh URL
  2. Increase artifact_download_timeout and check network reachability to the object-store host if status is 5xx
  3. Check object-store lifecycle/retention settings if 404s recur (artifacts cleaned before retrieval)
Defensive patterns

Strategy: retry

Type guard

def is_artifact_download_error(exc: BaseException) -> bool:
    return isinstance(exc, ArtifactDownloadError)

Try / catch

if res.status == ConversionStatus.FAILURE:
    if any('HTTP 403' in (e.error_message or '') for e in res.errors):
        res = retry_conversion(source)  # fresh presigned URL

Prevention

When it happens

Trigger: convert()/convert_all() materializing an artifact whose presigned URL has expired (S3/Xpiry headers), the object was deleted, or the endpoint returns 4xx/5xx.

Common situations: Long queue times so presigned URLs expire before download; clock skew; object lifecycle rules deleting artifacts; load balancer errors on the artifact host.

Related errors


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