docling-project/docling · error · ValueError

Invalid page range: start must be ≥ 1 and end must be ≥ star

Error message

Invalid page range: start must be ≥ 1 and end must be ≥ start.

What it means

The PageRange type is a Tuple[int, int] with an AfterValidator that enforces a 1-based, ordered range: the start must be >= 1 and the end must be >= the start. It is used for settings like page_range when converting documents, and the default is (1, sys.maxsize). Any out-of-order or zero/negative bound raises this ValueError at settings construction.

Source

Thrown at docling/datamodel/settings.py:12

import sys
from contextlib import contextmanager
from pathlib import Path
from typing import Annotated, Iterator, Optional, Tuple

from pydantic import AfterValidator, BaseModel
from pydantic_settings import BaseSettings, SettingsConfigDict


def _validate_page_range(v: Tuple[int, int]) -> Tuple[int, int]:
    if v[0] < 1 or v[1] < v[0]:
        raise ValueError(
            "Invalid page range: start must be ≥ 1 and end must be ≥ start."
        )
    return v


PageRange = Annotated[Tuple[int, int], AfterValidator(_validate_page_range)]

DEFAULT_PAGE_RANGE: PageRange = (1, sys.maxsize)


class DocumentLimits(BaseModel):
    max_num_pages: int = sys.maxsize
    max_file_size: int = sys.maxsize
    page_range: PageRange = DEFAULT_PAGE_RANGE


class BatchConcurrencySettings(BaseModel):
    doc_batch_size: int = 1  # Number of documents processed in one batch. Should be >= doc_batch_concurrency

View on GitHub (pinned to 61d76f1ff3)

Solutions

  1. Use 1-based inclusive bounds with start <= end, e.g. page_range=(3, 10).
  2. Clamp computed ranges: page_range=(max(1, start), max(start, end)).
  3. For 'up to the last page', pass the default (1, sys.maxsize) or omit page_range.

Example fix

# before
result = converter.convert(pdf, page_range=(0, 5))  # 0 is invalid

# after
result = converter.convert(pdf, page_range=(1, 5))
Defensive patterns

Strategy: validation

Validate before calling

def valid_page_range(r: tuple[int, int]) -> bool:
    start, end = r
    return start >= 1 and end >= start

assert valid_page_range((start, end)), "page_range must be 1-based with start <= end"

Type guard

def is_valid_page_range(r: tuple[int, int]) -> bool:
    s, e = r
    return isinstance(s, int) and isinstance(e, int) and 1 <= s <= e

Try / catch

try:
    converter.convert(doc, page_range=(s, e))
except ValidationError as e:
    if "Invalid page range" in str(e):
        s2, e2 = max(1, s), max(max(1, s), e)
        result = converter.convert(doc, page_range=(s2, e2))
    else:
        raise

Prevention

When it happens

Trigger: Passing page_range=(0, 10), page_range=(5, 3), or page_range=(-1, 5) to DocumentConverter.convert / convert_all, or setting a page-range-typed field on a settings object. Tuple order matters: it is (start, end), not (end, start).

Common situations: Treating page ranges as 0-based (coming from Python slice conventions) and passing start=0; swapping the tuple elements so start > end; computing end dynamically (e.g. num_pages - 2) and going below start on very short documents.

Related errors


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