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
- Assign a unique index to every TextCell at creation, e.g. enumerate(cells) and set cell.index = i.
- If integrating a custom OCR engine, map each detected text box to its position in the detection output order.
- 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
- Always assign cell.index = i from enumerate() when constructing TextCell lists.
- In OCR adapters, propagate the detection-order index into TextCell.index.
- Add an assertion for index uniqueness in tests of custom table pipelines.
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
- Cannot specify both ocr_preset and ocr_custom_config.
- Unsupported EasyOCR language code: {language}
- Invalid RapidOCR model spec {value!r}. Expected '<backend>:<
- Unknown RapidOCR backend {backend!r} in {value!r}. Supported
- Invalid RapidOCR model spec {value!r}: {err}
AI-assisted analysis of docling-project/docling@61d76f1ff3 (2026-08-14).
Data as JSON: /api/errors/94131118714fb609.
Report an issue: GitHub.