docling-project/docling · error · ArtifactDownloadError

Refusing to download artifact from a non-public URL: {url}.

Error message

Refusing to download artifact from a non-public URL: {url}.

What it means

SSRF guard: _validate_artifact_url raises ArtifactDownloadError when the artifact URL (initial or any redirect hop) does not resolve to a globally routable address — non-http(s) scheme, missing host, DNS failure, or a private/loopback/link-local/reserved/multicast IP. The check is skipped only when the client was constructed with _allow_private_artifact_urls=True.

Source

Thrown at docling/service_client/client.py:1951

                            )
                        chunks: list[bytes] = []
                        total = 0
                        async for chunk in response.aiter_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

    def _validate_artifact_url(self, url: str) -> None:
        if self._allow_private_artifact_urls:
            return
        if not _is_safe_artifact_url(url):
            raise ArtifactDownloadError(
                f"Refusing to download artifact from a non-public URL: {url}."
            )

    @staticmethod
    def _next_redirect_url(current_url: str, response: httpx.Response) -> str:
        location = response.headers.get("location")
        if not location:
            raise ArtifactDownloadError(
                "Artifact download redirect is missing a Location header."
            )
        return str(httpx.URL(current_url).join(location))

    def _check_artifact_size(self, total: int) -> None:
        if total > self._max_artifact_download_bytes:
            raise ArtifactDownloadError(
                "Artifact exceeds max_artifact_download_bytes "
                f"({self._max_artifact_download_bytes} bytes)."
            )

View on GitHub (pinned to 61d76f1ff3)

Solutions

  1. If the private endpoint is trusted, construct the client with _allow_private_artifact_urls=True (opt-in escape hatch)
  2. Better: expose the object store on a publicly routable address or run the client inside the same network with the flag set deliberately
  3. Check that the redirect chain does not detour through an internal load balancer

Example fix

// before
client = DoclingServiceClient('https://docling.internal')
# ArtifactDownloadError: Refusing to download artifact from a non-public URL

// after (trusted internal MinIO)
client = DoclingServiceClient('https://docling.internal', _allow_private_artifact_urls=True)
Defensive patterns

Strategy: validation

Validate before calling

from docling.service_client.client import _is_safe_artifact_url
# before opting out, check what the client will see:
assert _is_safe_artifact_url(presigned_url), 'URL resolves private — needs opt-in'

Type guard

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

Try / catch

try:
    results = list(client.convert_all(sources))
except ArtifactDownloadError as exc:
    if 'non-public URL' in str(exc):
        client = DoclingServiceClient(url, _allow_private_artifact_urls=True)  # deliberate trust decision

Prevention

When it happens

Trigger: docling-serve returns presigned URLs pointing at an internal hostname (e.g. minio.internal, 10.x.x.x, 169.254.169.254) while the client runs elsewhere; or a redirect hop lands on a private address.

Common situations: Self-hosted docling-serve in Docker/Kubernetes where the object store (MinIO) is only reachable inside the cluster; DNS resolving to a private IP from the client's network; a compromised service attempting SSRF via redirects.

Related errors


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