docling-project/docling · error · ValueError

ZIP URLs are not accepted on the convert endpoint

Error message

ZIP URLs are not accepted on the convert endpoint

What it means

The HttpSourceRequest model used by the convert endpoints runs a field validator on url that rejects any HTTP URL whose path (case-insensitive, query string stripped) ends in '.zip'. The convert endpoint processes single documents, while ZIP archives are handled by a different flow (the service defines a zip target/response container), so ZIP URLs are refused at request validation time.

Source

Thrown at docling/datamodel/service/requests.py:57


class FileSourceRequest(FileSource):
    kind: Literal["file"] = "file"


class AnyHttpSourceRequest(HttpSource):
    kind: Literal["http"] = "http"


class HttpSourceRequest(AnyHttpSourceRequest):
    """HTTP source for convert endpoints — rejects ZIP URLs."""

    @field_validator("url")
    @classmethod
    def reject_zip_url(cls, value: AnyHttpUrl) -> AnyHttpUrl:
        path = str(value).lower().split("?", maxsplit=1)[0]
        if path.endswith(".zip"):
            raise ValueError("ZIP URLs are not accepted on the convert endpoint")
        return value


class S3SourceRequest(S3Coordinates):
    kind: Literal["s3"] = "s3"


class AzureBlobSourceRequest(AzureBlobCoordinates):
    kind: Literal["azure_blob"] = "azure_blob"


class GoogleCloudStorageSourceRequest(GoogleCloudStorageCoordinates):
    kind: Literal["google_cloud_storage"] = "google_cloud_storage"


class GoogleDriveSourceRequest(GoogleDriveCoordinates):
    kind: Literal["google_drive"] = "google_drive"

View on GitHub (pinned to 61d76f1ff3)

Solutions

  1. Use the endpoint/flow that accepts ZIP archives instead of the convert endpoint.
  2. If the ZIP contains one document, download and extract it client-side, then send the extracted file (or its extracted URL) to convert.
  3. If the .zip suffix is incidental (URL rewrite), serve the file under a URL that reflects its real extension.

Example fix

# before
req = {
    "sources": [{"kind": "http", "url": "https://cdn.example.com/batch.zip"}],
    "options": {...},
}

# after
# extract locally, then convert each document
with zipfile.ZipFile(download("https://cdn.example.com/batch.zip")) as zf:
    for name in zf.namelist():
        convert_bytes(zf.read(name))
Defensive patterns

Strategy: type-guard

Validate before calling

from urllib.parse import urlparse

def is_zip_url(url: str) -> bool:
    return urlparse(url).path.lower().endswith(".zip")

if is_zip_url(source_url):
    raise ValueError("convert endpoint rejects .zip URLs; use the ZIP ingest flow")

Type guard

def is_convertible_http_url(url: str) -> bool:
    return not urlparse(url).path.lower().endswith(".zip")

Try / catch

try:
    resp = client.convert(sources=[{"kind": "http", "url": url}])
except ValidationError as e:
    if "ZIP URLs" in str(e):
        # extract locally or switch to the ZIP-capable flow
        ...
    raise

Prevention

When it happens

Trigger: Submitting a convert request whose http source url is e.g. 'https://example.com/docs/batch.ZIP' or '.../file.zip?token=abc'. The validator lowercases the URL and strips the query before checking the '.zip' suffix, so case tricks and query strings do not bypass it.

Common situations: Pointing the convert endpoint at a bulk-download ZIP of many documents; a CDN or export link that ends in .zip even for a single file; porting a curl example from the ZIP-ingest endpoint to the convert endpoint.

Related errors


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