RescoreTopNQuery throws (AssertionError with -ea, ArrayIndexOutOfBoundsException with -da) instead of matching nothing when its inner query matches no document: rewrite passes an empty top-n list to DocAndScoreQuery.createDocAndScoreQuery, which requires a non-empty one
Finding LFV-297Area: search / rescoringPriority: mediumImpact: crashNovelty: known upstream
AI-assisted finding, confirmed by automated checks. Upstream status: Fixed, by Greg Miller (Lucene committer, PR #16926) on 2026-10-28. Greg Miller fixed it on main and branch_11x (11.0) by returning MatchNoDocsQuery when the inner query leaves nothing to rescore, the same guard as our fix. They left it out of 10.6 because they were not sure it is severe enough, so the 10.x line keeps the bug. Source. This site files nothing upstream.
What it shows
You wrap a query in RescoreTopNQuery, for example full-precision rescoring of a kNN query with a filter, and on this index the inner query matches nothing (the filter matches no document, the term is absent, or the index is empty). You expect 0 hits, which is what the inner query alone returns. Instead the search throws an AssertionError with assertions enabled, or ArrayIndexOutOfBoundsException 'Index 0 out of bounds for length 0' without them, because rewrite() hands its empty top-n list to a helper that reads the first entry; the kNN query's own rewrite checks for this case and returns MatchNoDocsQuery.
Expected vs actual
- Expected: You expect 0 hits, which is what the inner query alone returns.
- Actual: The search throws an AssertionError with assertions enabled, or ArrayIndexOutOfBoundsException 'Index 0 out of bounds for length 0' without them, because rewrite() hands its empty top-n list to a helper that reads the first entry; the kNN query's own rewrite checks for this case and returns MatchNoDocsQuery.
Files
Run it
- Tested on Lucene main ea57360912ed (2026-10-24): reproduced.
- How to read the outcome: a failing test is the finding reproducing (the failure message states expected against actual).
- Seeds: a rerun with the same seed replays the same random choices. Assertions off (-da -dsa -Dtests.asserts=false) is the production-like setting.
# Lucene checkout at the tested commit, built with: ./gradlew --no-daemon assemble (JDK 25)
LUCENE=/path/to/lucene; V=11.0.0-SNAPSHOT
CP=$LUCENE/lucene/core/build/libs/lucene-core-$V.jar
CP=$CP:$LUCENE/lucene/codecs/build/libs/lucene-codecs-$V.jar
CP=$CP:$LUCENE/lucene/test-framework/build/libs/lucene-test-framework-$V.jar
CP=$CP:randomizedtesting-runner-2.9.2.jar:junit-4.13.2.jar:hamcrest-3.0.jar # test-framework dependencies (Maven Central)
CP=$CP:junit-platform-console-standalone-6.1.3.jar # JUnit Platform console launcher (Maven Central)
javac -d classes -cp "$CP" $(find src -name '*.java')
# run 1: assertions off (production-like), seed DEADBEEF
java -da -dsa -Dtests.asserts=false --add-modules jdk.incubator.vector --enable-native-access=ALL-UNNAMED -Dtests.seed=DEADBEEF \
-cp "classes:$CP" org.junit.platform.console.ConsoleLauncher \
execute --select-class org.apache.lucene.search.TestRescoreTopNQueryNoCandidates
Gradle alternative: copy the test into the matching module's src/test/ tree of the checkout and run ./gradlew -p lucene/<module> test --tests <class> -Ptests.seed=<seed> (add -Ptests.asserts=false for a run without assertions).
Feedback
Open an issue in lucene-ai-bug-hunt. Please do not open issues or pull requests in apache/lucene about this finding unless a Lucene committer agrees it should be filed.
Snapshot of 2026-11-03. AI-assisted; triaged by a Lucene maintainer as noted above. Feedback: open an issue in lucene-ai-bug-hunt.