Summary
After updating to docling-core==2.74.1 (via docling==2.89.0), Docling rejects Azure Blob SAS URLs before attempting the download when the Blob hostname resolves to a private IP through Azure Private Link.
This appears to come from the URL safety validation in docling_core.utils.file.resolve_source_to_stream(), which rejects any URL whose hostname resolves to a non-global IP.
Environment
docling==2.89.0
docling-core==2.74.1
- Python
3.13
- Windows
11 locally
- Azure Blob Storage SAS URL
- Storage account uses Azure Private Endpoint /
privatelink.blob.core.windows.net
Actual behavior
Calling DocumentConverter.convert() with a valid Azure Blob SAS URL fails with:
ValueError: URL is not allowed: https://<storage-account>.blob.core.windows.net/<container>/<document>.pdf?se=<expiry>&sp=r&sv=<api-version>&sr=b&sig=<redacted>
Expected behavior
Docling should allow the URL, or provide a supported opt-out / allowlist mechanism for trusted private hosts used behind Azure Private Endpoints.
The SAS URL is valid and intentionally points to a private Azure Storage endpoint. From the application point of view, this is a legitimate remote document source.
Minimal reproduction
from docling.document_converter import DocumentConverter
converter = DocumentConverter()
converter.convert(
"https://<storage-account>.blob.core.windows.net/<container>/<document>.pdf?se=<expiry>&sp=r&sv=<api-version>&sr=b&sig=<redacted>"
)
This fails inside docling_core.utils.file.resolve_source_to_stream() before any fetch is attempted.
DNS evidence
In our environment, the Azure Blob host resolves like this:
<storage-account>.blob.core.windows.net CNAME
<storage-account>.privatelink.blob.core.windows.net A 10.x.x.x
And a minimal check shows the resolved IP is private:
import socket
import ipaddress
from urllib.parse import urlparse
url = "https://<storage-account>.blob.core.windows.net/<container>/<document>.pdf?se=<expiry>&sp=r&sv=<api-version>&sr=b&sig=<redacted>"
host = urlparse(url).hostname
ip = socket.gethostbyname(host)
print(host) # <storage-account>.blob.core.windows.net
print(ip) # 10.x.x.x
print(ipaddress.ip_address(ip).is_private) # True
Relevant code path
The rejection appears to come from this logic:
http_url = TypeAdapter(AnyHttpUrl).validate_python(source)
url_str = str(http_url)
if not _is_safe_url(url_str):
raise ValueError(f"URL is not allowed: {url_str}")
_is_safe_url() resolves the hostname and requires the resulting IP to be globally routable, which blocks Azure Storage accounts accessed through Private Link.
Why this is a problem
This makes Docling incompatible with a common enterprise Azure setup where Blob Storage is intentionally reachable only through private networking.
The URL is not untrusted in our case; it is generated by our application and protected with a SAS token. The current behavior blocks legitimate private infrastructure along with SSRF-style targets.
Questions
- Is this strict private-IP rejection intentional for all remote sources?
- Would you accept a configuration-based override, allowlist, or callback for trusted hosts?
Full stack trace
Traceback (most recent call last):
File ".../app/messaging/consumer.py", line 159, in _process_message
await self.message_handler(message_value)
File ".../entrypoints/conversion_worker/lifespan.py", line 24, in _handle_message
await asyncio.get_running_loop().run_in_executor(None, process_file_markdown, event.data.model_dump())
File ".../Lib/concurrent/futures/thread.py", line 59, in run
result = self.fn(*self.args, **self.kwargs)
File ".../.venv/Lib/site-packages/sentry_sdk/integrations/threading.py", line 179, in wrapped_fn
return fn(*args, **kwargs)
File ".../app/services/conversion.py", line 99, in process_file_markdown
markdown_content = convert_to_markdown_isolated(file_path=url, fast=fast)
File ".../entrypoints/conversion_worker/docling_isolated.py", line 359, in convert_to_markdown_isolated
return _get_docling_runtime_manager().convert_to_markdown(file_path=file_path, fast=fast)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File ".../entrypoints/conversion_worker/docling_isolated.py", line 303, in convert_to_markdown
markdown_content = client.convert_to_markdown(file_path=file_path, fast=fast)
File ".../app/clients/docling.py", line 396, in convert_to_markdown
result = converter.convert(file_path)
File ".../.venv/Lib/site-packages/pydantic/_internal/_validate_call.py", line 40, in wrapper_function
return wrapper(*args, **kwargs)
File ".../.venv/Lib/site-packages/pydantic/_internal/_validate_call.py", line 137, in __call__
res = self.__pydantic_validator__.validate_python(pydantic_core.ArgsKwargs(args, kwargs))
File ".../.venv/Lib/site-packages/docling/document_converter.py", line 388, in convert
return next(all_res)
File ".../.venv/Lib/site-packages/docling/document_converter.py", line 447, in convert_all
for conv_res in conv_res_iter:
^^^^^^^^^^^^^
File ".../.venv/Lib/site-packages/docling/document_converter.py", line 542, in _convert
for input_batch in chunkify(
~~~~~~~~^
conv_input.docs(self.format_to_options),
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
settings.perf.doc_batch_size,
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
):
^
File ".../.venv/Lib/site-packages/docling/utils/utils.py", line 15, in chunkify
for first in iterator:
^^^^^^^^
File ".../.venv/Lib/site-packages/docling/datamodel/document.py", line 452, in docs
resolve_source_to_stream(item, self.headers)
File ".../.venv/Lib/site-packages/docling_core/utils/file.py", line 119, in resolve_source_to_stream
raise ValueError(f"URL is not allowed: {url_str}")
ValueError: URL is not allowed: https://<storage-account>.blob.core.windows.net/<container>/<document>.pdf?se=<expiry>&sp=r&sv=<api-version>&sr=b&sig=<redacted>
Summary
After updating to
docling-core==2.74.1(viadocling==2.89.0), Docling rejects Azure Blob SAS URLs before attempting the download when the Blob hostname resolves to a private IP through Azure Private Link.This appears to come from the URL safety validation in
docling_core.utils.file.resolve_source_to_stream(), which rejects any URL whose hostname resolves to a non-global IP.Environment
docling==2.89.0docling-core==2.74.13.1311locallyprivatelink.blob.core.windows.netActual behavior
Calling
DocumentConverter.convert()with a valid Azure Blob SAS URL fails with:Expected behavior
Docling should allow the URL, or provide a supported opt-out / allowlist mechanism for trusted private hosts used behind Azure Private Endpoints.
The SAS URL is valid and intentionally points to a private Azure Storage endpoint. From the application point of view, this is a legitimate remote document source.
Minimal reproduction
This fails inside
docling_core.utils.file.resolve_source_to_stream()before any fetch is attempted.DNS evidence
In our environment, the Azure Blob host resolves like this:
And a minimal check shows the resolved IP is private:
Relevant code path
The rejection appears to come from this logic:
_is_safe_url()resolves the hostname and requires the resulting IP to be globally routable, which blocks Azure Storage accounts accessed through Private Link.Why this is a problem
This makes Docling incompatible with a common enterprise Azure setup where Blob Storage is intentionally reachable only through private networking.
The URL is not untrusted in our case; it is generated by our application and protected with a SAS token. The current behavior blocks legitimate private infrastructure along with SSRF-style targets.
Questions
Full stack trace