Skip to content

Redundant bbox assertion in DocumentToken.get_location crashes export_to_doctags on near-degenerate elements #620

Description

@rnevet

AI Generated Report:

Summary

DocumentToken.get_location() in docling_core/types/doc/tokens.py enforces bbox[0] <= bbox[2] and bbox[1] <= bbox[3] via assert, but the next 4 lines of the same method clamp the values with min() / max() — so the asserts are redundant with the downstream normalization the method already performs. Near-degenerate elements (e.g. a 1-pt rule whose layout-model regression produced a slightly inverted bbox) crash export_to_doctags(add_location=True) even though the method's own output would be valid.

Repro

We hit this on a ~927-page PDF when running export_to_doctags(add_location=True) on a 153-page chunk:

File ".../docling_core/types/doc/tokens.py", line 307, in get_location
    assert bbox[1] <= bbox[3], f"bbox[1]<=bbox[3] => {bbox[1]}<={bbox[3]}"
AssertionError: bbox[1]<=bbox[3] => 633.3925132751465<=625.6789665222168

The inversion is only 7.7 pt — essentially a near-zero-height element (likely a decorative rule, a tiny diacritic, or a layout-model float artifact). Probability of hitting at least one such element grows with page count, so it bites long documents in particular.

The asserts are redundant with the method's own clamp

docling-core main, docling_core/types/doc/tokens.py, the body of get_location:

assert bbox[0] <= bbox[2], f"bbox[0]<=bbox[2] => {bbox[0]}<={bbox[2]}"
assert bbox[1] <= bbox[3], f"bbox[1]<=bbox[3] => {bbox[1]}<={bbox[3]}"

x0 = bbox[0] / page_w
y0 = bbox[1] / page_h
x1 = bbox[2] / page_w
y1 = bbox[3] / page_h

x0_tok = DocumentToken.get_location_token(val=min(x0, x1), rnorm=xsize, self_closing=self_closing)
y0_tok = DocumentToken.get_location_token(val=min(y0, y1), rnorm=ysize, self_closing=self_closing)
x1_tok = DocumentToken.get_location_token(val=max(x0, x1), rnorm=xsize, self_closing=self_closing)
y1_tok = DocumentToken.get_location_token(val=max(y0, y1), rnorm=ysize, self_closing=self_closing)

The min / max calls already produce correctly-ordered location tokens regardless of input order. The asserts add no protection — they just kill export_to_doctags when the layout pipeline emits a slightly inverted box.

Suggested fix

Drop the asserts, or replace them with explicit normalization that mirrors the downstream min/max:

x0, x1 = sorted((bbox[0], bbox[2]))
y0, y1 = sorted((bbox[1], bbox[3]))

then divide by page_w / page_h and emit tokens. The same code/issue exists at docling_core/types/legacy_doc/tokens.py:177.

Environment

  • docling==2.93.0
  • docling-core==2.75.0
  • docling-ibm-models==3.13.2
  • Python 3.12

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions