fix: handle empty batches in detection and end-to-end predictors - #2138
Open
linhongyu510 wants to merge 1 commit into
Open
fix: handle empty batches in detection and end-to-end predictors#2138linhongyu510 wants to merge 1 commit into
linhongyu510 wants to merge 1 commit into
Conversation
Passing an empty page list crashed three levels deep instead of returning an empty result. `RecognitionPredictor` has always short-circuited on an empty input, and `OrientationPredictor` gained the same behaviour in mindee#2069, but the detection and end-to-end predictors did not, so filtering a batch down to nothing raised from internals that never mention the empty input: - `PreProcessor.batch_inputs` computes `num_batches == 0` correctly, then reads `samples[0]` to pick the tuple/tensor branch -> `IndexError` - `detach_scores` calls `zip(*(...))` over no boxes -> `ValueError: not enough values to unpack (expected 2, got 0)` - on the KIE path `invert_data_structure` reads `x[0]` -> `IndexError` Guard at the three public entry points rather than patching each internal, so the existing helpers keep their non-empty precondition. `DetectionPredictor` returns the shape its `return_maps` contract promises, and the end-to-end predictors return an empty document of their own type -- `Document` for `OCRPredictor`, `KIEDocument` for `KIEPredictor`, whose per-class page shape would otherwise be lost to the base class. Verified `ruff check`, `ruff format --check` and `mypy doctr/` clean; reverting the guards turns the new test red at `preprocessor/pytorch.py:73`. Co-authored-by: Claude <noreply@anthropic.com>
linhongyu510
force-pushed
the
fix/empty-batch-predictors
branch
from
September 5, 2026 08:42
7d8556e to
a11910c
Compare
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #2138 +/- ##
=======================================
Coverage 97.24% 97.24%
=======================================
Files 169 169
Lines 10040 10046 +6
=======================================
+ Hits 9763 9769 +6
Misses 277 277
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
DetectionPredictor,OCRPredictorandKIEPredictorraise on an empty page list instead of returning an empty result.RecognitionPredictorhas always short-circuited on empty input (recognition/predictor/pytorch.py:50), andOrientationPredictorgained the same behaviour in #2069 — these three were the remaining gap, so this follows that precedent rather than introducing a new convention.Filtering a batch down to nothing is ordinary caller code (skip already-processed pages, drop files that failed a check upstream), and today it fails from internals that never mention the empty input.
Reproduction
There are three separate failure points on the way down, which is why the fix is not a one-liner in a single helper:
preprocessor/pytorch.py:73num_batchesis correctly0, thensamples[0]is read to pick the tuple/tensor branch →IndexErrorutils/geometry.py:124zip(*(_detach(box) for box in boxes))over no boxes →ValueError: not enough values to unpack (expected 2, got 0)models/_utils.py:276{k: ... for k in x[0]}→IndexErrorI confirmed the ordering by fixing them one at a time: guarding
batch_inputsmoved the crash todetach_scores, and guarding that one let the whole call returnDocument(pages=[]).Fix
Guard at the three public entry points instead of patching each internal, so
batch_inputs,detach_scoresandinvert_data_structurekeep their non-empty precondition and stay simple.Two details worth flagging:
DetectionPredictorreturns the shape its ownreturn_mapscontract promises:[]normally,([], [])whenreturn_maps=True.KIEPredictorreturnsKIEDocument(pages=[]), notDocument(pages=[]).KIEDocumentsubclassesDocument, so returning the base class would type-check and pass anisinstanceassertion while silently dropping the per-class page shape. The test asserts the exact type for this reason — I verified it catches the degradation by deliberately returningDocumentthere and watching the test go red.Tests
Added
test_predictors_on_empty_batch, which covers detection (bothreturn_mapsvalues), recognition (already-correct, asserted so the three stay consistent), and both end-to-end predictors.The test is load-bearing: reverting the guards fails it at
preprocessor/pytorch.py:73.The errors and the one failure are pre-existing on this machine and unrelated: the errors are
requests.exceptions.ConnectionErrorfrom tests that download weights or fixtures, andtests/common/test_io.py::test_read_htmlfails withOSError: cannot load library(missing WeasyPrint system libs). I verified thetest_read_htmlfailure reproduces on a clean checkout viagit stash.AI assistance was used for this change; I reviewed every changed line and ran the commands above locally.