docling-project/docling · error · ValueError

_do_prediction_on_image_to_table: duplicate cell indices det

Error message

_do_prediction_on_image_to_table: duplicate cell indices detected ({len(cell_ids) - len(set(cell_ids))} duplicates). All TextCell.index values must be unique; ensure callers assign a distinct index to each cell (default index=-1 causes this).

What it means

The image-to-table structure predictor matches detected table cells to OCR TextCells via each cell's index. If two non-empty cells share the same index, the matching dict silently loses cells, so Docling validates uniqueness and raises this ValueError. The default TextCell index is -1, so forgetting to assign indices is the canonical cause (the message calls this out).

Source

Thrown at docling/models/stages/table_structure/table_structure_model.py:344

        scale = img_width / bbox_width if bbox_width > 0 else self.scale

        # The table box spans the entire cropped image
        tbl_box = [0.0, 0.0, float(img_width), float(img_height)]

        # Sanity-check: every non-empty cell must have a unique index so that
        # the predictor's matching dict (keyed by cell id) works correctly.
        # The most common mistake is leaving all indices at the default -1.
        non_empty_cells = [c for c in table_cluster.cells if len(c.text.strip()) > 0]
        cell_ids = [c.index for c in non_empty_cells]
        if len(cell_ids) != len(set(cell_ids)):
            msg = (
                f"_do_prediction_on_image_to_table: duplicate cell indices detected "
                f"({len(cell_ids) - len(set(cell_ids))} duplicates). "
                f"All TextCell.index values must be unique; ensure callers assign "
                f"a distinct index to each cell (default index=-1 causes this)."
            )
            _log.error(msg)
            raise ValueError(msg)

        # Translate cell coordinates from page space to image-local space
        tokens = []
        for c in table_cluster.cells:
            if len(c.text.strip()) > 0:
                new_cell = copy.deepcopy(c)
                cell_bbox = new_cell.rect.to_bounding_box()
                local_bbox = BoundingBox(
                    l=(cell_bbox.l - table_cluster.bbox.l) * scale,
                    t=(cell_bbox.t - table_cluster.bbox.t) * scale,
                    r=(cell_bbox.r - table_cluster.bbox.l) * scale,
                    b=(cell_bbox.b - table_cluster.bbox.t) * scale,
                    coord_origin=cell_bbox.coord_origin,
                )
                new_cell.rect = BoundingRectangle.from_bounding_box(local_bbox)
                tokens.append(
                    {
                        "id": new_cell.index,

View on GitHub (pinned to 61d76f1ff3)

Solutions

  1. Assign a unique index to every TextCell at creation, e.g. enumerate(cells) and set cell.index = i.
  2. If integrating a custom OCR engine, map each detected text box to its position in the detection output order.
  3. As an immediate diagnostic, assert len({c.index for c in cells if c.text.strip()}) == count before invoking the model.

Example fix

# before
cells = [TextCell(index=-1, text=t, rect=r) for t, r in ocr_results]

# after
cells = [TextCell(index=i, text=t, rect=r) for i, (t, r) in enumerate(ocr_results)]
Defensive patterns

Strategy: validation

Validate before calling

non_empty = [c for c in table_cluster.cells if c.text.strip()]
ids = [c.index for c in non_empty]
assert len(ids) == len(set(ids)), (
    f"duplicate TextCell indices: {len(ids) - len(set(ids))}; assign unique cell.index"
)

Type guard

def has_unique_cell_indices(cells: list) -> bool:
    ids = [c.index for c in cells if c.text.strip()]
    return len(ids) == len(set(ids))

Try / catch

try:
    table = model._do_prediction_on_image_to_table(scale, table_cluster, ...)
except ValueError as e:
    if "duplicate cell indices" in str(e):
        for i, c in enumerate(table_cluster.cells):
            c.index = i  # repair and retry once
        table = model._do_prediction_on_image_to_table(scale, table_cluster, ...)
    else:
        raise

Prevention

When it happens

Trigger: Building a TableCluster/TextCell list manually or from a custom OCR adapter where every cell keeps index=-1, then running the table structure model's image-to-table prediction; duplicates count is reported explicitly.

Common situations: Custom OCR engines or preprocessing code that constructs TextCell objects without setting index; copy-deepmodify flows that clone cells; adapters ported from older versions that relied on list position.

Related errors


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