docling-project/docling · error · DocumentLoadError
Could not load Box Note document with hash {self.document_ha
Error message
Could not load Box Note document with hash {self.document_hash}. What it means
Raised by the benchmark's post-conversion assertion in _run_benchmark() after converting a generated single-sheet workbook. The script builds exactly one worksheet whose merged ranges should be parsed by Docling's XLSX pipeline into exactly one TableModel, and treats any other table count as a failure of the assumption being benchmarked. It fires when DocumentConverter.convert() on the temporary .xlsx yields zero tables (conversion produced no table structure) or more than one (the sheet was split into multiple tables, e.g. per merged range or per region).
Source
Thrown at docling/backend/boxnote_backend.py:64
"""
@override
def __init__(self, in_doc: InputDocument, path_or_stream: BytesIO | Path):
super().__init__(in_doc, path_or_stream)
self.data: dict[str, Any] = {}
try:
raw = ""
if isinstance(self.path_or_stream, BytesIO):
raw = self.path_or_stream.getvalue().decode("utf-8")
elif isinstance(self.path_or_stream, Path):
raw = self.path_or_stream.read_text(encoding="utf-8")
if raw.strip():
loaded = json.loads(raw)
if isinstance(loaded, dict):
self.data = loaded
except (json.JSONDecodeError, UnicodeDecodeError, OSError) as e:
raise DocumentLoadError(
f"Could not load Box Note document with hash {self.document_hash}."
) from e
if "atext" in self.data and not self.is_valid():
raise DocumentLoadError(
"Legacy Box Notes (the pre-August-2022 atext/pool format) are not "
"supported yet; only the current Box Note format can be converted."
)
@override
def is_valid(self) -> bool:
return isinstance(self.data.get("doc"), dict)
@classmethod
@override
def supports_pagination(cls) -> bool:
return False
View on GitHub (pinned to 61d76f1ff3)
Solutions
- Print or inspect document.tables (count, origins, and each table's data.grid) before the raise to see whether the sheet was split or dropped entirely — that tells you which side of the contract changed.
- If you intentionally changed the XLSX backend's segmentation (e.g. one table per merged range), update the benchmark's expectation: replace the strict `len(document.tables) != 1` check with the new expected count, or assert against the sum of all tables' rows.
- If tables is empty, verify the installed docling matches this checkout (`pip show docling`, run via `uv run` from the repo) and that InputFormat.XLSX conversion isn't being skipped or raising a swallowed error; check converter.convert() status/errors on the ConversionResult.
- Pin/upgrade the docling version whose XLSX backend is known to emit one table per worksheet for this workbook shape, since the assertion encodes that version's behavior.
Example fix
// before (perfs/xlsx_merged_cells.py:148-152)
if len(document.tables) != 1:
raise RuntimeError(
f"expected one table for {merge_count} merges, got {len(document.tables)}"
)
table = document.tables[0]
// after - tolerate a backend that splits the sheet, assert on totals
table = document.tables[0] if document.tables else None
if table is None:
raise RuntimeError(f"no tables produced for {merge_count} merges")
expected_rows = merge_count * len(document.tables)
if sum(t.data.num_rows for t in document.tables) != expected_rows:
raise RuntimeError(
f"expected {expected_rows} total rows for {merge_count} merges "
f"across {len(document.tables)} tables"
) Defensive patterns
Strategy: validation
Validate before calling
document = converter.convert(workbook_path).document
table_count = len(document.tables)
if table_count == 0:
raise SystemExit("XLSX conversion produced no tables; check backend/format detection")
# expected: one table per worksheet in the current backend contract
if table_count != 1:
print(f"warning: sheet split into {table_count} tables", file=sys.stderr) Type guard
def has_single_table(document) -> bool:
"""Narrow a DoclingDocument expected to hold exactly one XLSX worksheet table."""
return len(document.tables) == 1 and document.tables[0].data is not None Try / catch
try:
document = converter.convert(workbook_path).document
except ConversionError as exc:
raise SystemExit(f"conversion failed for {workbook_path}: {exc}") from exc
if len(document.tables) != 1:
# record the deviation instead of aborting the whole benchmark sweep
tqdm.write(f"table count {len(document.tables)} for merge_count={merge_count}", file=sys.stderr) Prevention
- Pin the docling version you are benchmarking and record it (the script already emits docling_version + commit_sha per result line — diff those when results change).
- When upgrading docling or its backend dependencies, re-run the smallest merge_count first and inspect document.tables before launching the full sweep.
- Treat these assertions as the benchmark's contract: if you intentionally change XLSX table segmentation, update the assertion in the same commit.
When it happens
Trigger: Calling converter.convert(workbook_path).document on the generated workbook and inspecting len(document.tables) after a Docling upgrade that changes XLSX table segmentation (one table per worksheet vs. one per merged range/row group); an XLSX backend or openpyxl version change that stops emitting the sheet as a single table; the file being misdetected as a non-XLSX format so tables is empty; passing merge_count values that trigger different backend code paths (e.g. very large sheets hitting a splitting heuristic).
Common situations: Running the perf script on a Docling branch/PR that changes MsPowerpointDocumentBackend/XlsxDocumentBackend table emission; benchmarking after a dependency bump (docling-core, openpyxl, python-docx stack) where the XLSX backend's tables-per-sheet contract changed; running the script against an installed docling that differs from the checkout; adding multi-sheet or differently-shaped workbooks to the benchmark without updating the assertion.
Related errors
- Legacy Box Notes (the pre-August-2022 atext/pool format) are
- Could not initialize AsciiDoc backend for file with hash {se
AI-assisted analysis of docling-project/docling@61d76f1ff3 (2026-08-14).
Data as JSON: /api/errors/26ad65561a407aac.
Report an issue: GitHub.