All findings · Lucene finding · LFV-1018 · leafcollector-javadoc-example-misses-scoremode-and-scorable

LeafCollector's javadoc example does not compile: no scoreMode(), and setScorer(Scorer) instead of setScorer(Scorable)

The only code example in the LeafCollector javadoc has not compiled since 5.1.0: javac reports a missing scoreMode() and a setScorer(Scorer) that does not implement setScorer(Scorable). Documentation only; with those two mismatches fixed, it works as described.

Summary

Priority
Low, rank 1238The 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
minor
Since
5.1.0 (LUCENE-6218, 2015-02-05); the second error since 8.0.0 (LUCENE-6228)introduced 2015-02-05 · over 11 years
Affected
5.1.0-10.6.0 and main
Status
documented
Found by
non-formal: compiling the javadoc code examples against main

AI-assisted page: this finding was first signalled by code reading or testing and then checked with AI tools; the checks it went through, and what each found, are under Prior reports, history and checks. Not yet triaged by a human. Snapshot of 2026-11-06.

The finding in 30 seconds

A javadoc example that teaches doc-ID re-basing in a custom Collector cannot compile against the two interfaces it implements.

Background: Collector and LeafCollector

  1. A Collector receives the hits of a search. IndexSearcher asks its scoreMode() whether scores are needed, then, for each index segment, asks getLeafCollector(LeafReaderContext) for a LeafCollector.
  2. The LeafCollector is handed the segment's scorer through setScorer(Scorable) and then gets collect(int doc) for every hit. doc is relative to the segment; adding the segment's docBase gives the index-wide doc ID.
Diagram of the bug. The LeafCollector javadoc example is an anonymous Collector with only getLeafCollector, whose anonymous LeafCollector declares setScorer(Scorer) and collect(int doc) with bits.set(docBase + doc). The interfaces at main require more: Collector has the abstract scoreMode(), and LeafCollector has the abstract setScorer(Scorable). javac reports two errors: no scoreMode() at the example's line 38, and setScorer(Scorable) not implemented at line 43. With scoreMode() returning COMPLETE_NO_SCORES and a Scorable parameter, the example compiles and, on 3 segments with docBase 0, 2 and 5, collects global doc IDs 0, 3, 4 and 5.
The javadoc example next to the two interfaces it must implement: the missing 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.

The finding, step by step

  1. You want to write your own Collector, and you copy the 21-line example from the LeafCollector class javadoc: an anonymous Collector whose LeafCollector calls bits.set(docBase + doc) to turn each segment's local doc IDs into index-wide ones.
  2. 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.
  3. 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 anonymous LeafCollector "does not override abstract method setScorer(org.apache.lucene.search.Scorable)".
  4. Why: Collector.scoreMode() is abstract (since 8.0.0; from 5.1.0 to 7.x it was needsScores()), and since 8.0.0 setScorer takes a Scorable, so the example's setScorer(Scorer) is only an overload. Neither change updated the example, which compiled on 5.0.0.
  5. Fix: add scoreMode() returning ScoreMode.COMPLETE_NO_SCORES and take a Scorable. The corrected example compiles and collects exactly {0, 3, 4, 5}.

Who is affected

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.

The example (main 645d4fe14a20, LeafCollector.java:36-56)

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);
      }

    };
  }

});

The two mismatches

  1. No scoreMode(). The anonymous Collector implements only getLeafCollector (:40-54). Collector.java:56 declares ScoreMode scoreMode(); with no default (only setWeight has one, :63), so javac reports <anonymous Example$1> is not abstract and does not override abstract method scoreMode() in org.apache.lucene.search.Collector at searcher.search(query, new Collector() { (:38).
  2. setScorer(Scorer) is an overload, not an override. The interface declares void 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.LeafCollector at return new LeafCollector() { (:43). The other interface methods have defaults, and collect(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.

Why the build never noticed

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.

Adjacent stale text (same javadoc, not separate bugs)

  • :31-32 "recording the docBase from the most recent setNextReader call": that method no longer exists; the example itself correctly reads context.docBase in getLeafCollector.
  • :26 "hold onto the passed {@link Scorer} instance, and call {@link Scorer#score()}" and :68 "save the passed-in Scorer": the parameter is a Scorable.
Exposure and likelihood167 words
  • Who is hit. Anyone who writes a custom Collector from this example. It is the one worked example of doc-ID re-basing in the collector docs. Collector is marked Expert and both interfaces are @lucene.experimental (Collector.java:23, :44; LeafCollector.java:62), and the Collector javadoc steers most users to SimpleCollector or 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 Scorable change (lucene/MIGRATE.md:1474).

Minimal repro

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.

TestLeafCollectorJavadocExample.java (JUnit, one test)83 lines
/*
 * 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);
  }
}
Command
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 method scoreMode() in Collector, and <anonymous Example$1$1> does not override abstract method setScorer(Scorable) in LeafCollector; the same with assertions off.

All files of this repro: repro/LFV-1018/

More files, environment, full output282 words
  • The test compiles the 21 lines of LeafCollector.java:36-56 verbatim (only the <em> markup removed) with javax.tools, against java.class.path. Example.java:10 and :15 in the output are the example's lines 38 and 43. The deprecation note comes from the search(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 indexReader and a Query query and 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.

Environment

lucenemain 645d4fe14a20 (11.0.0-SNAPSHOT); the same test also fails on the published 10.6.0, 10.5.2 and 9.12.3 jars
jdkOpenJDK 25.0.3 (build 25.0.3+9-LTS)
osLinux, aarch64
Command (environment)
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)

Observed output

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)

Why it looks unintended

  • The same file contradicts it: the example's setScorer(Scorer) sits 25 lines above the interface's setScorer(Scorable), and Collector.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 into LeafCollector; 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, removed acceptsDocsOutOfOrder()).
  • 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 from needsScores() to scoreMode() (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.

Suggested fix

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_SCORES is 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, seed DEADBEEF, 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 test passes (google-java-format, ecj lint, forbidden APIs, regeneration checksums; error-prone skipped), and ./gradlew :lucene:core:renderJavadoc passes 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.

C. Drop the code and point to SimpleCollector (not run)

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 docBase re-basing, which is the point of the paragraph.

What the checks validated

Not run (nothing to run).

Downstream note

No downstream runtime relevance: the broken example cannot compile, so no shipped application can contain it unrepaired.

How it was found

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 645d4fe14a20 jars 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.

Prior reports, history and checks

Prior reports

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 of search(Query, Collector); #16805 does not touch LeafCollector.java).
  • Not fixed upstream: upstream main f815e679bf6c (2026-11-01) has the same LeafCollector.java blob as 645d4fe14a20; 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.java or search/Collector.java.
What was searched, when, and what turned up
WhereWhenHowFound
apache/lucene issues, PRs and commits; the JIRA archive2026-10-29scripted 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 -8488no report; #10170 and #9534 related
Mailing lists, downstream projects, web2026-10-2920 list queries over dev@, java-user@, issues@ and commits@, 6 threads read; Elasticsearch, OpenSearch, Solr and the .NET port; 6 web queriesno report; a 2015 java-user reply points a user to this example as guidance
apache/lucene (recheck)2026-11-01upstream 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; recheckedno 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

History

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 new LeafCollector; first release 5.0.0, where it compiles.
  • Broken: 010e352f603 (trunk) and its branch_5x backport 8c0dbb90087, both 2015-02-05, Robert Muir, LUCENE-6218 (#7280), added the abstract Collector.needsScores() without touching LeafCollector.java. First affected release 5.1.0.
  • Kept broken (8.0.0): 4fc5a872ded (LUCENE-4100, #5172, Adrien Grand, 2017-12-07) replaced needsScores() with the abstract scoreMode(); 910a0231f6f (LUCENE-6228, #7290, Alan Woodward, 2018-09-03) changed setScorer(Scorer) to setScorer(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.
Versions and branches executed, with results
Version or commitVerdictResult
main 645d4fe14a20 affected2 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 affected2 errors: scoreMode() and setScorer(Scorable)
8.0.0 affected2 errors (import-only port of the test)
5.1.0, 7.7.3 affected1 error: needsScores() not implemented (import-only port)
5.0.0 not affectedthe verbatim example compiles (import-only port)

Independent checks

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.

Each independent check and its verdict
CheckVerdictWhat it found
cross-model review, refute lens (OpenAI GPT-5.6, executing) real, high confidenceReproduced; root cause agrees; the fix fixes it; the intended argument is weak.
independent reproducer realIts 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 confidenceStrongest 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 LowRe-ran the repro; a structurally wrong API example, not a typo.
credibility audit confirmed, LowRe-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 validatedThrough 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 correctedOne 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.

Diagram file

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.

Sources and provenance

Finding record, Lucene and upstream sources, repro files and diagram notes1,319 words

Finding record

LFV-1018, snapshot of 2026-11-06

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 test and :lucene:core:renderJavadoc); option B compile-checked. Published release jars 9.12.3, 10.5.2 and 10.6.0 (release tag commit 4ae946e5a9b), and branch_10x 657014b3c579, 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.java is byte-identical on 645d4fe14a20 and on upstream main f815e679bf6c (2026-11-01) (read).

Lucene and upstream sources

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)

Other statements trace to the hunt's working files (models, logs, reviews and briefs), which are not published.

Repro files

About the diagrams

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.

Built 2026-11-06 from the hunt's record of this finding and its doc file. Lucene main under test in this hunt: ea57360912ed. Every listed finding was re-run on Lucene main e1faeba08bde on 2026-11-02.