A javadoc example that teaches doc-ID re-basing in a custom Collector cannot compile against the two interfaces it implements.
- A
Collectorreceives the hits of a search.IndexSearcherasks itsscoreMode()whether scores are needed, then, for each index segment, asksgetLeafCollector(LeafReaderContext)for aLeafCollector. - The
LeafCollectoris handed the segment's scorer throughsetScorer(Scorable)and then getscollect(int doc)for every hit.docis relative to the segment; adding the segment'sdocBasegives the index-wide doc ID.
scoreMode() and the setScorer(Scorer) overload, the two javac errors they cause, and the corrected example re-basing doc IDs across 3 segments (docBase 0, 2 and 5). Select the still to open it at full size. Generated with AI, checked against the code.- You want to write your own
Collector, and you copy the 21-line example from theLeafCollectorclass javadoc: an anonymousCollectorwhoseLeafCollectorcallsbits.set(docBase + doc)to turn each segment's local doc IDs into index-wide ones. - You expect it to compile and, on an index of 3 segments with 2, 3 and 1 documents where local docs 0 | 1, 2 | 0 match, to set bits {0, 3, 4, 5}: docBase 0, 2 and 5 plus the local IDs.
- Instead javac reports 2 errors, on main, 10.6.0, 10.5.2 and 9.12.3: the anonymous
Collector"does not override abstract method scoreMode()", and the anonymousLeafCollector"does not override abstract method setScorer(org.apache.lucene.search.Scorable)". - Why:
Collector.scoreMode()is abstract (since 8.0.0; from 5.1.0 to 7.x it wasneedsScores()), and since 8.0.0setScorertakes aScorable, so the example'ssetScorer(Scorer)is only an overload. Neither change updated the example, which compiled on 5.0.0. - Fix: add
scoreMode()returningScoreMode.COMPLETE_NO_SCORESand take aScorable. The corrected example compiles and collects exactly {0, 3, 4, 5}.
Developers who implement Collector directly (an Expert, @lucene.experimental API) and start from this example, the only one in the LeafCollector javadoc, on any release from 5.1.0 to 10.6.0 or main. Nothing wrong ever runs: javac refuses the code and names the missing pieces. Low impact, but plainly wrong.
Mechanism, terms and numbers469 words
The LeafCollector class javadoc explains that collect(int) receives segment-relative doc IDs and shows "a simple example showing how to collect docIDs into a BitSet" (LeafCollector.java:30-57 on main 645d4fe14a20). Copied into code, it does not compile on any release since 5.1.0: the anonymous Collector lacks the abstract scoreMode() (Collector.java:56; needsScores() from 5.1.0 to 7.x), and the anonymous LeafCollector declares setScorer(Scorer) (:46) where the interface, a few lines below, declares setScorer(Scorable) (:71), so the example's method is only an overload. The example compiled when it was written for 5.0.0; LUCENE-6218 (5.1.0), LUCENE-4100 and LUCENE-6228 (both 8.0.0) changed the API and did not update it. Adding scoreMode() returning ScoreMode.COMPLETE_NO_SCORES and taking a Scorable makes it compile, and the corrected example collects exactly the expected global doc IDs. No runtime code is wrong.
IndexSearcher searcher = new IndexSearcher(indexReader);
final BitSet bits = new BitSet(indexReader.maxDoc());
searcher.search(query, new Collector() {
public LeafCollector getLeafCollector(LeafReaderContext context)
throws IOException {
final int docBase = context.docBase;
return new LeafCollector() {
// ignore scorer
public void setScorer(Scorer scorer) throws IOException {
}
public void collect(int doc) throws IOException {
bits.set(docBase + doc);
}
};
}
});- No
scoreMode(). The anonymousCollectorimplements onlygetLeafCollector(:40-54).Collector.java:56declaresScoreMode scoreMode();with no default (onlysetWeighthas one,:63), so javac reports<anonymous Example$1> is not abstract and does not override abstract method scoreMode() in org.apache.lucene.search.Collectoratsearcher.search(query, new Collector() {(:38). setScorer(Scorer)is an overload, not an override. The interface declaresvoid setScorer(Scorable scorer)(:71).Scorer extends Scorable(Scorer.java:28), and a method with a narrower parameter type does not implement it, so javac reports<anonymous Example$1$1> is not abstract and does not override abstract method setScorer(org.apache.lucene.search.Scorable) in org.apache.lucene.search.LeafCollectoratreturn new LeafCollector() {(:43). The other interface methods have defaults, andcollect(int)is implemented.
The rest is valid: context.docBase is a public field (LeafReaderContext.java:28), and the deprecated IndexSearcher.search(Query, Collector) (IndexSearcher.java:634-638) still exists and only adds a deprecation note. indexReader and query are illustrative free names and not part of the claim.
The build's javadoc checks do not compile the code in this <pre> block. On main, the example's line 46 is the only setScorer(Scorer left in any .java file under lucene/, and the open issue #9534 (LUCENE-8488) asks for such a compile check in general.
:31-32"recording the docBase from the most recent setNextReader call": that method no longer exists; the example itself correctly readscontext.docBaseingetLeafCollector.:26"hold onto the passed {@link Scorer} instance, and call {@link Scorer#score()}" and:68"save the passed-in Scorer": the parameter is aScorable.
Exposure and likelihood167 words
- Who is hit. Anyone who writes a custom
Collectorfrom this example. It is the one worked example of doc-ID re-basing in the collector docs.Collectoris marked Expert and both interfaces are@lucene.experimental(Collector.java:23, :44;LeafCollector.java:62), and theCollectorjavadoc steers most users toSimpleCollectoror the built-in collectors (Collector.java:26-28). - Default vs opt-in. Not a configuration matter: every copy fails. javac fails on each release compiled (5.1.0, 7.7.3, 8.0.0, 9.12.3, 10.5.2, 10.6.0, branch_10x and main), and a class-file scan finds the needed API members missing in all 109 builds from 5.1.0 to main.
- Scale. One example, 2 compile errors. LUCENE-9129 (#10170) shows a user hitting the same
scoreMode()javac error in their own collector after upgrading from 7.x to 8.x, so the API change did trip up at least one user, although that report does not name the example. - What it does not do. No wrong results, no runtime path, no index change. MIGRATE.md documents the
Scorablechange (lucene/MIGRATE.md:1474).
One JUnit test compiles the example, copied verbatim from the javadoc, with the JDK's compiler against the Lucene jars on the test class path. It adds only the imports, a small enclosing class and method, and the free names indexReader and query as that method's parameters. Deterministic, single thread, about 1.6 s; the same with assertions on and off.
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.lucene.search;
import java.net.URI;
import java.util.List;
import javax.tools.DiagnosticCollector;
import javax.tools.JavaFileObject;
import javax.tools.SimpleJavaFileObject;
import javax.tools.ToolProvider;
import org.apache.lucene.tests.util.LuceneTestCase;
public class TestLeafCollectorJavadocExample extends LuceneTestCase {
// Expected: the LeafCollector class-javadoc example compiles; actual: javac reports missing scoreMode() and setScorer(Scorable).
public void testClassJavadocExampleCompiles() throws Exception {
String source =
"""
import java.io.IOException;
import java.util.BitSet;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.LeafReaderContext;
import org.apache.lucene.search.*;
class Example {
void example(IndexReader indexReader, Query query) throws IOException {
IndexSearcher searcher = new IndexSearcher(indexReader);
final BitSet bits = new BitSet(indexReader.maxDoc());
searcher.search(query, new Collector() {
public LeafCollector getLeafCollector(LeafReaderContext context)
throws IOException {
final int docBase = context.docBase;
return new LeafCollector() {
// ignore scorer
public void setScorer(Scorer scorer) throws IOException {
}
public void collect(int doc) throws IOException {
bits.set(docBase + doc);
}
};
}
});
}
}
""";
JavaFileObject unit =
new SimpleJavaFileObject(URI.create("string:///Example.java"), JavaFileObject.Kind.SOURCE) {
@Override
public CharSequence getCharContent(boolean ignoreEncodingErrors) {
return source;
}
};
DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<>();
List<String> options =
List.of(
"-classpath", System.getProperty("java.class.path"),
"-d", createTempDir().toString(),
"-proc:none");
boolean compiled =
ToolProvider.getSystemJavaCompiler()
.getTask(null, null, diagnostics, options, null, List.of(unit))
.call();
assertTrue(diagnostics.getDiagnostics().toString(), compiled);
}
}
cp TestLeafCollectorJavadocExample.java $LUCENE/lucene/core/src/test/org/apache/lucene/search/
cd $LUCENE && ./gradlew :lucene:core:test --tests org.apache.lucene.search.TestLeafCollectorJavadocExample -Ptests.seed=DEADBEEF
# assertions off: add -Ptests.asserts=false (same result)
- Expected
- The example compiles, as it did on 5.0.0 and as the corrected example does on main.
- Observed
- javac reports 2 errors:
<anonymous Example$1>does not override abstract methodscoreMode()inCollector, and<anonymous Example$1$1>does not override abstract methodsetScorer(Scorable)inLeafCollector; the same with assertions off.
More files, environment, full output282 words
- The test compiles the 21 lines of
LeafCollector.java:36-56verbatim (only the<em>markup removed) withjavax.tools, againstjava.class.path.Example.java:10and:15in the output are the example's lines 38 and 43. The deprecation note comes from thesearch(Query, Collector)call; it is a note, not an error. - Even simpler for a reader: paste the 21 lines into any method that has an
IndexReader indexReaderand aQuery queryand compile; javac prints the same 2 errors. - Results: fails 1 of 1 on main with assertions on and off, standalone (1.62 s) and through gradle; also fails on 10.6.0, 10.5.2, 9.12.3 and branch_10x
657014b3c579. Before 9.0 the test framework package differs, so the history runs on 5.0.0, 5.1.0, 7.7.3 and 8.0.0 used an import-only port. - As a regression test: none is needed for a javadoc-only fix, and this test would not guard the javadoc anyway, because it compiles a copy of the text. The general guard is the tooling #9534 asks for.
| lucene | main 645d4fe14a20 (11.0.0-SNAPSHOT); the same test also fails on the published 10.6.0, 10.5.2 and 9.12.3 jars |
|---|---|
| jdk | OpenJDK 25.0.3 (build 25.0.3+9-LTS) |
| os | Linux, aarch64 |
cp TestLeafCollectorJavadocExample.java $LUCENE/lucene/core/src/test/org/apache/lucene/search/
cd $LUCENE && ./gradlew :lucene:core:test --tests org.apache.lucene.search.TestLeafCollectorJavadocExample -Ptests.seed=DEADBEEF
# assertions off: add -Ptests.asserts=false (same result)
TestLeafCollectorJavadocExample > testClassJavadocExampleCompiles FAILED
java.lang.AssertionError: [/Example.java:10: error: <anonymous Example$1> is not abstract and does not override abstract method scoreMode() in org.apache.lucene.search.Collector
searcher.search(query, new Collector() {
^, /Example.java:15: error: <anonymous Example$1$1> is not abstract and does not override abstract method setScorer(org.apache.lucene.search.Scorable) in org.apache.lucene.search.LeafCollector
return new LeafCollector() {
^, Note: /Example.java uses or overrides a deprecated API., Note: Recompile with -Xlint:deprecation for details.]
at __randomizedtesting.SeedInfo.seed([DEADBEEF:9A7EBD6A0F12FB11]:0)
at org.junit.Assert.fail(Assert.java:89)
at org.junit.Assert.assertTrue(Assert.java:42)
at org.apache.lucene.search.TestLeafCollectorJavadocExample.testClassJavadocExampleCompiles(TestLeafCollectorJavadocExample.java:81)- The same file contradicts it: the example's
setScorer(Scorer)sits 25 lines above the interface'ssetScorer(Scorable), andCollector.scoreMode()is abstract. The javadoc calls it "a simple example". - It compiled on 5.0.0, and maintainers updated this example on earlier API changes (2009, 2012, 2015).
- The API changes were deliberate (the
scoreMode()switch was listed only as an optimization); none updated the example.
The full argument168 words
- Contract text.
LeafCollector.java:32-33: "Here's a simple example showing how to collect docIDs into a BitSet:".:71:void setScorer(Scorable scorer) throws IOException;.Collector.java:55-56: "Indicates what features are required from the scorer."ScoreMode scoreMode();(abstract). - History. LUCENE-5527 (
85642f59ddf, 2014) moved the example intoLeafCollector; it compiles on 5.0.0 (executed). Earlier maintenance of the same example:5f611724642(2009, "The example impl in Collector missed the new method acceptsDocsOutOfOrder, added"),647b5091697(2012, "fix out of date api usage"),927827a215a(2015, LUCENE-6179, removedacceptsDocsOutOfOrder()). - API changes that skipped it. CHANGES.txt: "LUCENE-6218, LUCENE-6220: Add Collector.needsScores()" (
lucene/CHANGES.txt:11080); MIGRATE.md: "LeafCollector.setScorer() now takes a Scorable rather than a Scorer (LUCENE-6228)" (lucene/MIGRATE.md:1474). The switch fromneedsScores()toscoreMode()(LUCENE-4100) has no MIGRATE entry and was listed only under "Optimizations" in the 8.0.0 CHANGES (as Tomoko Uchida pointed out on #10170). LUCENE-6228 (910a0231f6f) edited this very file and left the example. - Tests. No test compiles javadoc examples; #9534 (LUCENE-8488, open) asks for that in general.
The devil's-advocate check146 words
Strongest case for "intended" (verifiers built it): Collector is Expert and @lucene.experimental and points users to SimpleCollector; the snippet is a sketch (the free names indexReader and query); its entry point search(Query, Collector) is deprecated and slated for removal in 11.0 (#12892, listed in #16918; PR #16805 open); the errors are loud, CHANGES.txt lists the needsScores() addition and MIGRATE.md the Scorable change; the build does not compile javadoc <pre> blocks.
Why it fails: experimental status explains why the API changed, not why the same-version docs contradict the interface declared below them. The free names are context, while both errors are about missing API members. The deprecated call still compiles on every release. Maintainers updated this very example on earlier API moves. Every verifier and the adjudication rejected the "intended" reading; an impact audit passed it as a structurally wrong API example, not a typo.
Recommended A. Fix the example in place (scoreMode() + Scorable), plus the three stale passages
Fix the example in place: add public ScoreMode scoreMode() { return ScoreMode.COMPLETE_NO_SCORES; } to the anonymous Collector and change the parameter to Scorable scorer; in the same patch, fix the three stale passages (setNextReader and the two "Scorer" mentions). Validated: the patched example compiles, and the 5 closest core collector suites, static checks and the javadoc build pass.
--- a/lucene/core/src/java/org/apache/lucene/search/LeafCollector.java
+++ b/lucene/core/src/java/org/apache/lucene/search/LeafCollector.java
* searcher.search(query, new Collector() {
*
+ * public ScoreMode scoreMode() {
+ * return ScoreMode.COMPLETE_NO_SCORES;
+ * }
+ *
* public LeafCollector getLeafCollector(LeafReaderContext context)
@@
* <em>// ignore scorer</em>
- * public void setScorer(Scorer scorer) throws IOException {
+ * public void setScorer(Scorable scorer) throws IOException {
* }All options and trade-offs3 options ยท 669 words
Three ways to resolve it; A is the smallest and was validated. On main, open PR #16805 would remove the search(Query, Collector) call that A keeps; B is the variant that survives it.
Backport: on 10.6.0 and branch_10x the example code is the same, but the <pre> markup differs (#15573 changed it on main), so the diff's first hunk does not apply as is there; the same text edits apply by hand, and the second hunk applies (re-run by the independent fact check).
A committer may also prefer a SimpleCollector-based example: doSetNextReader receives the LeafReaderContext (SimpleCollector.java:36) and setScorer(Scorable) is already a no-op (:39-41). It still needs scoreMode(). Not run.
A. Fix the example in place (scoreMode() + Scorable), plus the three stale passages (validated by the checks) (recommended)
Add scoreMode() returning ScoreMode.COMPLETE_NO_SCORES to the anonymous Collector, take Scorable scorer in setScorer, and in the same javadoc replace "recording the docBase from the most recent setNextReader call" with "recording the docBase of the {@link org.apache.lucene.index.LeafReaderContext} passed to {@link Collector#getLeafCollector}", and "Scorer" with "Scorable" where the text describes the setScorer parameter (class javadoc and the setScorer method javadoc). Javadoc only: 13 lines added, 8 removed, formatted with ./gradlew tidy.
--- a/lucene/core/src/java/org/apache/lucene/search/LeafCollector.java
+++ b/lucene/core/src/java/org/apache/lucene/search/LeafCollector.java
* searcher.search(query, new Collector() {
*
+ * public ScoreMode scoreMode() {
+ * return ScoreMode.COMPLETE_NO_SCORES;
+ * }
+ *
* public LeafCollector getLeafCollector(LeafReaderContext context)
@@
* <em>// ignore scorer</em>
- * public void setScorer(Scorer scorer) throws IOException {
+ * public void setScorer(Scorable scorer) throws IOException {
* }Trade-offs
COMPLETE_NO_SCORESis the honest mode: the example ignores scores and visits all matches ("Produced scorers will allow visiting all matches but scores won't be available",ScoreMode.java:25-26).- Smallest change; keeps the example's shape and its lesson (re-basing with
docBase). - Keeps the deprecated
search(Query, Collector)call: it compiles on branch_10x and on main today, but stops compiling if #16805 lands, so fix A on main would be short-lived. If #16805 is about to merge, B fits main and A fits branch_10x.
What the checks validated
- Gradle, scratch worktree of
645d4fe14a20, seedDEADBEEF, 2026-11-01: without the fix the repro fails with assertions on and off; with the diff, the patched example block is line-for-line identical (25 lines) to the corrected text compiled by the same test, and that test passes with assertions on and off. - Closest core suites with the diff:
TestMultiCollector,TestCollectorManager,TestTotalHitCountCollector,TestPositiveScoresOnlyCollector,TestCachingCollector: 5 classes, 22 tests, 0 failures. ./gradlew :lucene:core:check -x testpasses (google-java-format, ecj lint, forbidden APIs, regeneration checksums; error-prone skipped), and./gradlew :lucene:core:renderJavadocpasses with no warning lines in its log; the rendered page shows the corrected example.- At runtime the corrected example collects {0, 3, 4, 5} on a 3-segment index (independent reproducer's control, main and 9.12.3). The full Lucene test suite was not run (javadoc-only change).
B. The same corrected example without the search call (for main after #16805) (validated by the checks)
Replace the first three lines with final BitSet bits = new BitSet(indexReader.maxDoc()); and Collector collector = new Collector() {, keep the corrected body of A, and close with };.
* final BitSet bits = new BitSet(indexReader.maxDoc());
* Collector collector = new Collector() {
*
* public ScoreMode scoreMode() {
* return ScoreMode.COMPLETE_NO_SCORES;
* }
* ... getLeafCollector exactly as in A ...
* };Trade-offs
- Uses no deprecated API, so it survives the removal of
search(Query, Collector)(#12892, #16805). - No longer shows how to run the collector; on main that is
search(Query, CollectorManager), which needs per-collector state, more than this example should teach.
What the checks validated
Compile-checked: the same test with this text passes 1 of 1 on the stock main jars (2026-11-01). Not run at runtime.
Remove the <pre> block and refer readers to SimpleCollector and the built-in collectors, as the Collector javadoc already does.
Trade-offs
- Nothing left to drift.
- Loses the one worked illustration of
docBasere-basing, which is the point of the paragraph.
What the checks validated
Not run (nothing to run).
No downstream runtime relevance: the broken example cannot compile, so no shipped application can contain it unrepaired.
Non-formal: an AI tool extracted the code examples from the javadocs of Lucene's core search, index and geo packages and compiled each against main; this one failed with 2 errors. A one-test repro, a scan of 159 core jars for the example's API members and runs on releases from 5.0.0 to 10.6.0 then dated the breakage.
The method in full111 words
- A first probe compiled the block verbatim against the stock main
645d4fe14a20jars and got both errors (2026-10-29). - The hunt unit re-ran it, extracted the block mechanically, wrote the one-test repro and checked in which of 159 core jars (every release from 1.9.1 to 10.6.0, branch_10x and main) the example's API members exist, with a class-file scan whose boundaries were cross-checked with javac: met only in 5.0.0, unmet from 5.1.0 on.
- Corrected-example controls compiled and collected exactly the global hit doc IDs over a 3-segment index, with assertions on and off, so only the text is wrong.
- This is a compile check of documentation code, not model checking or a proof.
No prior report found in GitHub issues, PRs and commits, the JIRA archive, the mailing lists, downstream projects or the web (searched 2026-10-29, rechecked 2026-11-01). Nearest: #10170 (the same javac error in a user's own collector) and #9534 (a general wish to compile-check javadoc examples). Not fixed upstream.
Searches3 searches
- Related, not this bug: #10170 (LUCENE-9129: the same
scoreMode()javac message for a user's own collector after an upgrade; the example is not mentioned); #9534 (LUCENE-8488, open: compile-check javadoc examples in general, names none); #7290 (LUCENE-6228), #5172 (LUCENE-4100) and LUCENE-6218 (the changes that made it stale; no comment mentions the example); #6590 (LUCENE-5527, wrote it); #15573 (a markup sweep that edited the block and kept the code); #12892 and PR #16805 (removal ofsearch(Query, Collector); #16805 does not touchLeafCollector.java). - Not fixed upstream: upstream main
f815e679bf6c(2026-11-01) has the sameLeafCollector.javablob as645d4fe14a20; the last commit to the file is #16144 (2026-05-31, method javadoc only). Tracking issue #16937 does not name it. - Parallel javadoc sweep: open PR #16949 "Fix stale names and examples in javadocs" (updated 2026-10-30) edits 27 javadoc files, none of them
search/LeafCollector.javaorsearch/Collector.java.
| Where | When | How | Found |
|---|---|---|---|
| apache/lucene issues, PRs and commits; the JIRA archive | 2026-10-29 | scripted searches (16 issue/PR queries, 3 commit queries, 4 JIRA queries; 1406 distinct hits, top 40 read); manual searches for the example's phrases ("simple example showing how to collect", "ignore scorer", "most recent setNextReader") and for LeafCollector with scoreMode, Scorable and "does not compile"; the changed files of all open PRs; the threads of LUCENE-6218, -6228, -4100, -9129 and -8488 | no report; #10170 and #9534 related |
| Mailing lists, downstream projects, web | 2026-10-29 | 20 list queries over dev@, java-user@, issues@ and commits@, 6 threads read; Elasticsearch, OpenSearch, Solr and the .NET port; 6 web queries | no report; a 2015 java-user reply points a user to this example as guidance |
| apache/lucene (recheck) | 2026-11-01 | upstream main HEAD and the file's blob and commits; issues and PRs mentioning LeafCollector updated since 2026-10-29; the phrase "collect docIDs into a BitSet"; javadoc-example PRs updated since 2026-10-29 (the changed files of all 48 PRs updated since then were scanned by the independent fact check); the state of #16805; rechecked | no report; the file is unchanged on upstream main f815e679bf6c; #16805 still open and unmerged; two javadoc-example PRs were updated since then, #16904 and the open sweep #16949 "Fix stale names and examples in javadocs" (27 files), and neither touches LeafCollector.java or Collector.java |
Broken since 5.1.0 by LUCENE-6218 (010e352f603, 2015-02-05), which added the abstract needsScores(); LUCENE-4100 and LUCENE-6228 (8.0.0) turned it into today's 2 errors. 5.0.0 compiles the example; every release from 5.1.0 to 10.6.0 and main fails.
Versions tested5 rows
- Written:
85642f59ddf(LUCENE-5527, #6590, Adrien Grand, 2014-04-04) moved the example into the newLeafCollector; first release 5.0.0, where it compiles. - Broken:
010e352f603(trunk) and its branch_5x backport8c0dbb90087, both 2015-02-05, Robert Muir, LUCENE-6218 (#7280), added the abstractCollector.needsScores()without touchingLeafCollector.java. First affected release 5.1.0. - Kept broken (8.0.0):
4fc5a872ded(LUCENE-4100, #5172, Adrien Grand, 2017-12-07) replacedneedsScores()with the abstractscoreMode();910a0231f6f(LUCENE-6228, #7290, Alan Woodward, 2018-09-03) changedsetScorer(Scorer)tosetScorer(Scorable)in the same file and left the example. - The example's code is unchanged since
927827a215a(2015-01-15); #15573 (2026-01) changed only the<pre>markup. A class-file scan of 159 core jars finds the example's API needs met only in 5.0.0 and unmet in all 109 builds from 5.1.0 to main, including 8.8.2-8.11.4 by their published jars.
| Version or commit | Verdict | Result |
|---|---|---|
main 645d4fe14a20 | affected | 2 errors with assertions on and off (standalone and gradle); the patched example compiles with fix A |
10.6.0 (release tag commit 4ae946e5a9b), branch_10x 657014b3c579, 10.5.2, 9.12.3 | affected | 2 errors: scoreMode() and setScorer(Scorable) |
| 8.0.0 | affected | 2 errors (import-only port of the test) |
| 5.1.0, 7.7.3 | affected | 1 error: needsScores() not implemented (import-only port) |
| 5.0.0 | not affected | the verbatim example compiles (import-only port) |
Confirmed by an independent reproducer, reviews by a reviewer from another model family (OpenAI GPT-5.6), an intended-behaviour devil's advocate and an adjudication (high confidence); a credibility audit kept it as a low-priority doc defect. This doc's gradle run validated the fix.
Checks and verdicts320 words
Every check re-ran the repro on main 645d4fe14a20 or read the same block byte-identical; none found a reading under which the example is meant not to compile.
| Check | Verdict | What it found |
|---|---|---|
| cross-model review, refute lens (OpenAI GPT-5.6, executing) | real, high confidence | Reproduced; root cause agrees; the fix fixes it; the intended argument is weak. |
| independent reproducer | real | Its own test fails on main with assertions on and off, on 10.5.2 and 9.12.3; its corrected control compiles and collects global doc IDs {0, 3, 4, 5} on 3 segments (main and 9.12.3); javac sweep: 5.0.0 compiles, 5.1.0 and later fail. |
| cross-model review, intended lens (OpenAI GPT-5.6, executing) | real, high confidence | Strongest intended case built and rejected as weak; a separate re-run repeated its tests on 645d4fe14a20. |
| intended-behaviour devil's advocate | real, high confidence (Low, documentation) | Re-ran the repro on main and 10.5.2; flagged #16805 and the adjacent stale text. |
| adjudication | confirmed, high confidence, priority Low | Re-ran the repro; a structurally wrong API example, not a typo. |
| credibility audit | confirmed, Low | Re-ran the repro; impact minor confirmed as not inflated; one test, passes with the fixed text. |
| this doc's validation (2026-11-01) | fix A validated | Through gradle in a scratch worktree of 645d4fe14a20: the repro fails without the fix (assertions on and off); with fix A the patched example compiles, 5 closest collector suites (22 tests) pass, and :lucene:core:check -x test and renderJavadoc pass; option B compiles. |
| independent fact checks of this doc (2026-11-01) | bug real; wording corrected | One check re-ran the minimal repro on main (standalone and gradle, assertions on and off) and on the published 10.6.0, 10.5.2 and 9.12.3 jars and branch_10x: it fails every time with the 2 errors, and fix A passes. A second check by a reviewer from another model family (OpenAI GPT-5.6) agreed. Their wording corrections are applied in this doc. |
The diagram as a still image, generated with AI and checked against the code. It is under 10 MB, the limit for a GitHub attachment.
- PNG 454 KB
Finding record, Lucene and upstream sources, repro files and diagram notes1,319 words
- Registry title
- LeafCollector's class javadoc example ('collect docIDs into a BitSet') does not compile on any release since 5.1.0: its anonymous Collector has no scoreMode() (the abstract needsScores() before 8.0.0) and its setScorer(Scorer) does not implement LeafCollector.setScorer(Scorable) (documentation defect)
- Area
- core / search (LeafCollector class javadoc example)
- Priority
- Low, rank 1238: The core LeafCollector class javadoc example, the usual starting point for custom Collectors, fails loudly at compile time on every release since 5.1.0 with no runtime effect.
- Impact class
- minor
- Impact note
- Documentation only, with no runtime effect: a developer who writes a custom Collector (an extension point the Collector javadoc labels Expert, @lucene.experimental) and starts from the only code example in the LeafCollector class javadoc gets two compile errors on every release since 8.0.0 (one since 5.1.0) and has to find scoreMode() and Scorable in the interfaces or in MIGRATE.md; no configuration, scale or special input is involved.
- Summary
- The LeafCollector class javadoc explains that collect() receives segment-relative doc IDs and shows 'a simple example showing how to collect docIDs into a BitSet' with an anonymous Collector and LeafCollector. Copied into code on any current release it does not compile: the anonymous Collector does not implement the abstract scoreMode() (needsScores() from 5.1.0 to 7.x), and the anonymous LeafCollector does not implement setScorer(Scorable), because the example's setScorer(Scorer) has only been an overload since 8.0.0. With scoreMode() returning ScoreMode.COMPLETE_NO_SCORES and a Scorable parameter the example compiles and collects exactly the expected global doc IDs, so only the javadoc is wrong.
- Introduced
- 010e352f603 (trunk, svn r1657554; LUCENE-6218 'don't decode freqs or enumerate all positions when scoring is not needed', migrated as #7280, by Robert Muir (rmuir), author date = commit date 2015-02-05; branch_5x backport 8c0dbb90087, svn r1657571, same day, is the one released in 5.1.0): it added the abstract Collector.needsScores() and did not touch LeafCollector.java, so the example's anonymous Collector stopped compiling. Two later API changes kept it broken, neither touching the example: 4fc5a872ded (LUCENE-4100, #5172, Adrien Grand, author date 2017-12-07, 8.0.0) replaced needsScores() by the abstract scoreMode(), and 910a0231f6f (LUCENE-6228, #7290, Alan Woodward, author date 2018-09-03, 8.0.0) changed LeafCollector.setScorer(Scorer) to setScorer(Scorable), editing the declaration further down the same file and leaving the example's setScorer(Scorer) as a mere overload (the second javac error). The example itself was written by 85642f59ddf (LUCENE-5527, #6590, Adrien Grand, 2014-04-04) and was valid in 5.0.0; its code lines are unchanged since 927827a215a (LUCENE-6179, 2015-01-15) [read: git log, git show, git blame and git merge-base --is-ancestor in the full-history clone; exec: release runs, see affected_versions] (2015-02-05 (010e352f603 and its branch_5x backport 8c0dbb90087, author date = commit date, svn import); the second error since 2018-09-03 (910a0231f6f, LUCENE-6228, author date))
- First affected version
- 5.1.0 [exec] (the verbatim example compiles on 5.0.0 [exec] and fails on 5.1.0 with the needsScores() error); the current pair of errors (scoreMode() and setScorer(Scorable)) since 8.0.0 [exec]
- Affected versions
- every release from 5.1.0 to 10.6.0 (RC1 4ae946e5a9b), branch_10x 657014b3c579 and main 645d4fe14a20; not fixed upstream (upstream main HEAD still 645d4fe14a20 at 2026-10-29; the last commit touching LeafCollector.java, 14a3b87e75e0 (#16144, 2026-05-31), does not change the example). [exec: 5.0.0 compiles; 5.1.0 and 7.7.3 fail with the needsScores() error; 8.0.0, 9.12.3, 10.5.2, the 10.6.0 RC1, branch_10x and main (-ea and -da) fail with the scoreMode() and setScorer(Scorable) errors; the pre-9.0 runs use an import-only port of the test (LuceneTestCase package)] [read: a bytecode scan of 159 core jars finds the example's API requirements met only in 5.0.0 and unmet in all 109 builds from 5.1.0 to main, including 8.8.2-8.11.4 by their published jars; the example's code text is identical at 5.0.0, 5.1.0, 7.7.3, 8.0.0, 9.0.0, 9.12.3, 10.5.2 and main]. Before 5.0.0 LeafCollector does not exist (n/a; 4.x had a different example in the Collector javadoc). Caveat: all runs used JDK 25 on Linux aarch64.
- Status
- documented (brief, checks and a per-finding doc exist)
- Verification
- Confirmed by AI-run checks: an independent repro, a cross-model review that agreed, an intended-behaviour check, a prior-report search and a skeptical audit; not yet triaged by a person.
- Found by
- non-formal
- Upstream and prior reports
- none found (searched 2026-10-29; the finder's, the independent reproduction's and the intended-behaviour check's searches agree): scripted searches (16 GitHub issue and PR queries, 3 commit queries, 2 downstream-repository queries, 20 mailing-list queries over dev@, java-user@, issues@ and commits@, 4 JIRA-archive queries; 1406 distinct hits, top 40 reviewed), manual GitHub searches (apache/lucene, three downstream search-engine repositories, the .NET port), the changed files of all 415 open apache/lucene PRs (finder) and of the 48 PRs among the 61 items updated since 2026-10-27, the issue and PR streams of this week's outside javadoc-fix authors, JQL queries and the comment threads of LUCENE-6218, LUCENE-6228, LUCENE-4100, LUCENE-9129 and LUCENE-8488, list-thread reads and 6 web searches. Read but not the same bug: LUCENE-9129 (#10170: a user's own collector hit the same scoreMode() javac error after upgrading to 8.x; the example is not mentioned), LUCENE-8488 (#9534, open: a generic wish to compile-check javadoc examples, naming none), LUCENE-6228 (#7290, the change that left the Scorer half; its one javadoc remark concerns a proposed rename), LUCENE-4100 (#5172) and LUCENE-6218 (the other root-cause changes), LUCENE-5527 (#6590, wrote the example), #15573 (a markup sweep that edited this block and kept the stale code), #16144 (the latest change to the file, method javadoc only), #15225 (an umbrella issue on javadoc quality), #12991 (an unrelated NPE), and the open PRs #15382 (adds a default method further down the file) and #16805 (removes IndexSearcher.search(Query, Collector), which the example also calls; not a fix of this bug). Recheck: upstream main HEAD still 645d4fe14a20, no apache/lucene issue or PR updated since a recent cutoff, GitHub search for LeafCollector items updated since 2026-10-28 and for '"collect docIDs into a BitSet"' returns 0. Tracking issue #16937 does not name it.
- Lucene commit tested
- main
645d4fe14a20: the minimal repro with assertions on and off, standalone and through gradle; the recommended fix in a scratch worktree (the patched example compiled through the same test, the closest core collector suites,:lucene:core:check -x testand:lucene:core:renderJavadoc); option B compile-checked. Published release jars 9.12.3, 10.5.2 and 10.6.0 (release tag commit4ae946e5a9b), and branch_10x657014b3c579, with the minimal repro (independent fact check) and with the hunt's first test; an import-only port of the test on 5.0.0 (compiles), 5.1.0, 7.7.3 and 8.0.0.LeafCollector.javais byte-identical on645d4fe14a20and on upstream mainf815e679bf6c(2026-11-01) (read).
- Finding record
lucene/core/src/java/org/apache/lucene/search/LeafCollector.java lines 22-57 (the class javadoc and its example; line 46 'public void setScorer(Scorer scorer)') and line 71 ('void setScorer(Scorable scorer)'); Collector.java lines 22-56 (Expert, @lucene.experimental, abstract 'ScoreMode scoreMode();' at line 56); IndexSearcher.java lines 627-638 (search(Query, Collector), @Deprecated)
- The finding in 30 seconds
lucene/core/src/java/org/apache/lucene/search/LeafCollector.java (lines 23-64 the class javadoc and example, 71 setScorer(Scorable), 104-159 the default methods)lucene/core/src/java/org/apache/lucene/search/Collector.java (lines 23-56: Expert, @lucene.experimental, abstract scoreMode() at 56, default setWeight at 63)lucene/core/src/java/org/apache/lucene/search/Scorer.java (line 28) and IndexSearcher.java (lines 627-645, the deprecated search(Query, Collector))lucene/core/src/java/org/apache/lucene/index/LeafReaderContext.java (line 28)
- Who is affected
lucene/core/src/java/org/apache/lucene/search/Collector.java (lines 23-44) and LeafCollector.java (line 62)lucene/MIGRATE.md (line 1474)
- Why it looks unintended
lucene/core/src/java/org/apache/lucene/search/LeafCollector.java (lines 32-33, 46, 71) and Collector.java (lines 55-56)lucene/CHANGES.txt (line 11080) and lucene/MIGRATE.md (line 1474)a Lucene clone tag releases/lucene-solr/8.0.0: lucene/CHANGES.txt (8.0.0 section, LUCENE-4100 under Optimizations) and lucene/MIGRATE.txt (no scoreMode, needsScores or LUCENE-4100 entry); #10170 comment by Tomoko Uchida (2020-01-13)
- Suggested fix
lucene/core/src/java/org/apache/lucene/search/ScoreMode.java (lines 25-26)lucene/core/src/java/org/apache/lucene/search/SimpleCollector.java (lines 27-46)
- TestLeafCollectorJavadocExample.java (JUnit, one test):
repro/LFV-1018/TestLeafCollectorJavadocExample.java(83 lines)
Made from the code at 645d4fe14a20 and an executed probe (the published example fails with the two errors; the corrected one compiles and collects across 3 leaves with docBase 0, 2 and 5), in parallel with this doc.
assets/LFV-1018/bug.png(454 KB)
