All findings · Lucene finding · LFV-989 · analysis-package-info-examples-use-pre-5-0-analysis-api

The TokenStream API tutorial in the analysis package overview has seven examples that do not compile

Seven code examples in Lucene's TokenStream API tutorial, the org.apache.lucene.analysis package overview, do not compile on any release since 6.0.0: they use constructors removed in 4.4 and 5.0, subclass the final StandardAnalyzer and omit reflectWith. Documentation only: no Lucene code behaves differently, and every failure is loud.

Summary

Priority
Low, rank 1237The core analysis tutorial's seven examples fail loudly at compile time with no runtime effect, and it is the most-read of the pages with broken examples.
Impact
minor
Since
3.0.0 for one example; all seven since 6.0.0 (five API changes, mainly LUCENE-5859 in 5.0)introduced 2009-10-21 · over 17 years
Affected
3.0.0 to 10.6.0 and main
Status
documented
Found by
non-formal: a compile sweep of the code examples in Lucene's javadoc

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

Lucene's core analysis package overview teaches the TokenStream API with code you are meant to copy. Most of it dates from Lucene 3 and 4; later API changes broke seven examples, and no edit since has fixed the stale calls.

Background: the analysis API in one minute

  1. An Analyzer turns text into tokens: a Tokenizer splits the text (WhitespaceTokenizer splits at spaces) and TokenFilters change or drop tokens (LengthFilter(source, 3, Integer.MAX_VALUE) drops tokens shorter than 3 characters). Each token carries attributes, such as its text, implemented by AttributeImpl classes.
  2. Until 4.x most of these constructors took a Version argument. Lucene 5.0 removed it (LUCENE-5859), and since 6.0 every AttributeImpl must implement reflectWith (LUCENE-6651).
Diagram of the bug: the first example of the org.apache.lucene.analysis package overview calls new StandardAnalyzer(matchVersion), and javac answers 'no suitable constructor found for StandardAnalyzer(Version)', because StandardAnalyzer today has only the constructors (), (CharArraySet) and (Reader) and is final. The page's other examples fail the same way: WhitespaceTokenizer(Version), LengthFilter(true, ...), FilteringTokenFilter(Version, ...), a subclass of the final StandardAnalyzer, and a custom attribute without reflectWith. With the stale calls replaced, the examples compile and run.
The tutorial's examples against today's API: what javac says for each of the seven, the API change that left the page stale, and the fix. Select the still to open it at full size. Generated with AI, checked against the code.

The finding, step by step

  1. You open the org.apache.lucene.analysis package overview, where TokenStream's javadoc sends you for example code, and copy its "Adding a LengthFilter" step: new WhitespaceTokenizer(matchVersion) and new LengthFilter(true, source, 3, Integer.MAX_VALUE).
  2. You expect it to compile and print what the page shows: the input "This is a demo of the TokenStream API" without the words of 2 or fewer characters.
  3. Instead javac rejects both lines: WhitespaceTokenizer lost its Version constructor in 5.0, and LengthFilter's only constructor is now (TokenStream, int, int); the page's (true, source, ...) form went in 4.4.
  4. All seven examples fail to compile: besides these two calls, StandardAnalyzer(Version) is gone, StandardAnalyzer is final, the LengthFilter listing calls the removed super(version, in), and the custom attribute does not implement reflectWith. Every release since 6.0.0 ships all seven broken.
  5. Rewritten against today's API (no Version argument, a small Analyzer instead of a StandardAnalyzer subclass, reflectWith, a final tagging filter), all seven compile, and the tutorial prints its outputs, such as This demo the TokenStream API.

Who is affected

Everyone who learns to write an Analyzer, TokenFilter or Attribute from this page and copies an example: javac rejects it, with no setting involved. A 2016 Stack Overflow question shows a user hitting example 1. The errors are loud and at compile time, and no Lucene code behaves differently; the cost is developer time.

Mechanism, terms and numbers505 words

The org.apache.lucene.analysis package overview (core package-info.java) is Lucene's tutorial for writing analyzers, token filters and attributes, and TokenStream's javadoc points readers to it for example code. Seven of its examples do not compile on main or on any release since 6.0.0: they call StandardAnalyzer(Version), WhitespaceTokenizer(Version) and FilteringTokenFilter(Version, TokenStream), removed in 5.0, and new LengthFilter(true, source, 3, Integer.MAX_VALUE), a signature gone since 4.4; one subclasses StandardAnalyzer, which is final; and the custom PartOfSpeechAttributeImpl lacks reflectWith(AttributeReflector), abstract since 6.0. The APIs changed on purpose, and no edit of the page since has fixed the stale calls. Written against the current API (no Version argument, new LengthFilter(source, 3, Integer.MAX_VALUE), a small Analyzer instead of a StandardAnalyzer subclass, a reflectWith method, and a final tagging filter so the tutorial also runs with assertions on), every example compiles and the tutorial prints the page's outputs.

The seven examples (lines of package-info.java at main 645d4fe14a20)

#examplestale codejavac saysbroken since
1Invoking the Analyzer, :181-203new StandardAnalyzer(matchVersion) (:183)no suitable constructor found for StandardAnalyzer(Version)5.0.0
2position gap, :265-272new StandardAnalyzer(matchVersion) { getPositionIncrementGap ... } (:267)the same, and cannot inherit from final StandardAnalyzer3.0.0
3whitespace tokenization, :606-645new WhitespaceTokenizer(matchVersion) (:617)no suitable constructor found for WhitespaceTokenizer(Version)5.0.0
4adding a LengthFilter, :669-676:672 as #3; new LengthFilter(true, source, 3, Integer.MAX_VALUE) (:673)constructor LengthFilter cannot be applied to given types (required: TokenStream,int,int)4.4.0
5how LengthFilter is implemented, :691-721super(version, in) (:709)constructor FilteringTokenFilter cannot be applied to given types (required: TokenStream)5.0.0
6a custom attribute, :808-832PartOfSpeechAttributeImpl with only clear() and copyTo() (:809)is not abstract and does not override abstract method reflectWith(AttributeReflector)6.0.0
7the final chain, :873-881the two lines of #4 (:876-877)as #44.4.0

Errors that come only from the page's placeholders (Version.LUCENE_XY, which the page asks you to substitute, and names it leaves undeclared) are not counted.

The current API

  • StandardAnalyzer.java:34: public final class StandardAnalyzer, constructors (CharArraySet) :46, () :51, (Reader) :61.
  • WhitespaceTokenizer.java:34-66 (analysis-common): (), (AttributeFactory), (int), (AttributeFactory, int).
  • LengthFilter.java:44 (analysis-common): only (TokenStream in, int min, int max). FilteringTokenFilter.java:38: only (TokenStream in).
  • AttributeImpl.java:34, :93, :99: clear(), reflectWith(AttributeReflector) and copyTo(AttributeImpl) are abstract. Analyzer has no Version API left.

Knock-on problems a fix should cover

  • MyAnalyzer takes a Version (:609-613), but the last step calls new MyAnalyzer() (:903).
  • :834-836 says the attribute "implements its abstract methods clear() and copyTo()".
  • public static class PartOfSpeechTaggingFilter (:842) is not final. TokenStream requires final subclasses or a final incrementToken(), "checked when Java assertions are enabled" (TokenStream.java:74-76), so even the compile-fixed tutorial throws AssertionError under -ea.
  • The four printed outputs list a token new (:658, :684, :889, :934) that the input text (:622, :901) does not contain.
Exposure and likelihood177 words
  • Who. Developers learning the TokenStream API from Lucene's own tutorial, the page TokenStream's javadoc points to ("You can find some example code for the new API in the analysis package level Javadoc", TokenStream.java:68).
  • Evidence of use. Stack Overflow question 40052828 (2016-10-14): a 6.2.1 user quoted example 1 and asked why it does not compile; the answer explained that the Version argument was removed in 5.0.
  • Default vs opt-in. No configuration: it is the page as published in the core module's javadoc.
  • Scale. Seven of the page's example blocks (the others are fragments, illustrative classes with undefined names, or compile as they are; the last step's main, :898-925, fails in the assembled tutorial only because it calls new MyAnalyzer() against the page's own MyAnalyzer(Version), a knock-on of example 3). All seven are broken in every release from 6.0.0 to 10.6.0 and main; at least one since 3.0.0.
  • Severity. Documentation only, loud, compile time. Once the compile errors are fixed, the part-of-speech steps also fail under -ea because the tagging filter is not final.

Minimal repro

One JUnit test, one method: it compiles the page's stale lines, copied from the page (the placeholder LUCENE_XY replaced by LATEST), with the JDK compiler against the core and analysis-common jars, and asserts no errors. Deterministic, single thread, about 1 s.

TestAnalysisOverviewExamplesCompile.java (JUnit, one test)98 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.analysis;

import java.net.URI;
import java.util.List;
import java.util.Locale;
import javax.tools.Diagnostic;
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 TestAnalysisOverviewExamplesCompile extends LuceneTestCase {

  private static final String EXAMPLES =
      """
      import org.apache.lucene.analysis.Analyzer;
      import org.apache.lucene.analysis.FilteringTokenFilter;
      import org.apache.lucene.analysis.TokenStream;
      import org.apache.lucene.analysis.Tokenizer;
      import org.apache.lucene.analysis.core.WhitespaceTokenizer;
      import org.apache.lucene.analysis.miscellaneous.LengthFilter;
      import org.apache.lucene.analysis.standard.StandardAnalyzer;
      import org.apache.lucene.util.AttributeImpl;
      import org.apache.lucene.util.Version;

      abstract class Examples extends Analyzer {
        Version matchVersion = Version.LATEST;
        Analyzer analyzer = new StandardAnalyzer(matchVersion);
        Analyzer myAnalyzer = new StandardAnalyzer(matchVersion) {
          public int getPositionIncrementGap(String fieldName) {
            return 10;
          }
        };

        @Override
        protected TokenStreamComponents createComponents(String fieldName) {
          final Tokenizer source = new WhitespaceTokenizer(matchVersion);
          TokenStream result = new LengthFilter(true, source, 3, Integer.MAX_VALUE);
          return new TokenStreamComponents(source, result);
        }
      }

      class Listing {
        abstract class LengthFilter extends FilteringTokenFilter {
          public LengthFilter(Version version, TokenStream in, int min, int max) {
            super(version, in);
          }
        }
      }

      final class PartOfSpeechAttributeImpl extends AttributeImpl {
        @Override
        public void clear() {}

        @Override
        public void copyTo(AttributeImpl target) {}
      }
      """;

  public void testExamplesCompile() throws Exception {
    JavaFileObject source =
        new SimpleJavaFileObject(URI.create("string:///Examples.java"), JavaFileObject.Kind.SOURCE) {
          @Override
          public CharSequence getCharContent(boolean ignoreEncodingErrors) {
            return EXAMPLES;
          }
        };
    DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<>();
    List<String> options = List.of("-proc:none", "-d", createTempDir().toString());
    ToolProvider.getSystemJavaCompiler()
        .getTask(null, null, diagnostics, options, null, List.of(source))
        .call();
    List<String> errors =
        diagnostics.getDiagnostics().stream()
            .filter(d -> d.getKind() == Diagnostic.Kind.ERROR)
            .map(d -> "line " + d.getLineNumber() + ": " + d.getMessage(Locale.ROOT))
            .toList();
    // lines copied from the analysis package-info.java examples (LUCENE_XY -> LATEST) must compile
    assertEquals(List.of(), errors);
  }
}
Command
# build the jars (any build that produces them, for example ./gradlew assemble)
L=$LUCENE/lucene
CP=$L/core/build/libs/lucene-core-11.0.0-SNAPSHOT.jar:$L/codecs/build/libs/lucene-codecs-11.0.0-SNAPSHOT.jar
CP=$CP:$L/analysis/common/build/libs/lucene-analysis-common-11.0.0-SNAPSHOT.jar
CP=$CP:$L/test-framework/build/libs/lucene-test-framework-11.0.0-SNAPSHOT.jar
CP=$CP:randomizedtesting-runner-2.9.2.jar:junit-4.13.2.jar:hamcrest-3.0.jar   # from the Gradle cache
mkdir -p $REPRO/classes
javac -d $REPRO/classes -cp $CP $REPRO/TestAnalysisOverviewExamplesCompile.java
java -ea -Dtests.seed=DEADBEEF -cp $REPRO/classes:$CP org.junit.runner.JUnitCore org.apache.lucene.analysis.TestAnalysisOverviewExamplesCompile
# assertions off: -da instead of -ea (the same 7 errors)
Expected
javac reports no errors: the seven examples compile.
Observed
7 javac errors, among them no suitable constructor found for StandardAnalyzer(Version), cannot inherit from final StandardAnalyzer and PartOfSpeechAttributeImpl is not abstract and does not override abstract method reflectWith(AttributeReflector); the same with -da.

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

More files, environment, full output402 words

Source line 13 of the in-memory file is package-info.java:183; 14-18 are :267-271; 22-23 are :672-673 (also :617 and :876-877); 31 is :709; 36-41 are :809-831 reduced to its two overrides. The 7 errors cover the seven examples: two StandardAnalyzer(Version) calls, the final class, WhitespaceTokenizer(Version), LengthFilter(true, ...), super(version, in) and the missing reflectWith.

Why a plain JUnit run and not ./gradlew test: the in-process compiler reads the test JVM's class path, which must hold both lucene-core and lucene-analysis-common. In :lucene:analysis:common Lucene is on the module path, so the compiler sees neither jar; :lucene:core tests lack analysis-common.

The test is not needed to show the defect: one javac error per example does that. As a regression test (optional for a javadoc fix): the same method with EXAMPLES holding the fixed lines passes with assertions on and off; it cannot see the page itself, so it only guards the new lines against a future API change.

Environment

lucenemain 645d4fe14a20 (11.0.0-SNAPSHOT); the verifiers' tests fail the same way on 10.5.2 and 9.12.3
jdkOpenJDK 25.0.3 (build 25.0.3+9-LTS)
osLinux, aarch64
Command (environment)
# build the jars (any build that produces them, for example ./gradlew assemble)
L=$LUCENE/lucene
CP=$L/core/build/libs/lucene-core-11.0.0-SNAPSHOT.jar:$L/codecs/build/libs/lucene-codecs-11.0.0-SNAPSHOT.jar
CP=$CP:$L/analysis/common/build/libs/lucene-analysis-common-11.0.0-SNAPSHOT.jar
CP=$CP:$L/test-framework/build/libs/lucene-test-framework-11.0.0-SNAPSHOT.jar
CP=$CP:randomizedtesting-runner-2.9.2.jar:junit-4.13.2.jar:hamcrest-3.0.jar   # from the Gradle cache
mkdir -p $REPRO/classes
javac -d $REPRO/classes -cp $CP $REPRO/TestAnalysisOverviewExamplesCompile.java
java -ea -Dtests.seed=DEADBEEF -cp $REPRO/classes:$CP org.junit.runner.JUnitCore org.apache.lucene.analysis.TestAnalysisOverviewExamplesCompile
# assertions off: -da instead of -ea (the same 7 errors)

Observed output

# plain JUnit run, main 645d4fe14a20, seed DEADBEEF, -ea (the same with -da); abridged: the JUnit version, NOTE,
# WARNING and timing lines and most stack frames omitted, javac's lists of non-applicable candidate constructors
# shortened to [...]
There was 1 failure:
1) testExamplesCompile(org.apache.lucene.analysis.TestAnalysisOverviewExamplesCompile)
java.lang.AssertionError: expected:<[]> but was:<[line 13: no suitable constructor found for StandardAnalyzer(org.apache.lucene.util.Version)
    [...], line 14: no suitable constructor found for StandardAnalyzer(org.apache.lucene.util.Version)
    [...], line 14: cannot inherit from final org.apache.lucene.analysis.standard.StandardAnalyzer, line 22: no suitable constructor found for WhitespaceTokenizer(org.apache.lucene.util.Version)
    [...], line 23: constructor LengthFilter in class org.apache.lucene.analysis.miscellaneous.LengthFilter cannot be applied to given types;
  required: org.apache.lucene.analysis.TokenStream,int,int
  found:    boolean,org.apache.lucene.analysis.Tokenizer,int,int
  reason: actual and formal argument lists differ in length, line 31: constructor FilteringTokenFilter in class org.apache.lucene.analysis.FilteringTokenFilter cannot be applied to given types;
  required: org.apache.lucene.analysis.TokenStream
  found:    org.apache.lucene.util.Version,org.apache.lucene.analysis.TokenStream
  reason: actual and formal argument lists differ in length, line 36: PartOfSpeechAttributeImpl is not abstract and does not override abstract method reflectWith(org.apache.lucene.util.AttributeReflector) in org.apache.lucene.util.AttributeImpl]>
	at __randomizedtesting.SeedInfo.seed([DEADBEEF:456DAE84982F4242]:0)
	at org.junit.Assert.fail(Assert.java:89)
	...
	at org.apache.lucene.analysis.TestAnalysisOverviewExamplesCompile.testExamplesCompile(TestAnalysisOverviewExamplesCompile.java:96)

FAILURES!!!
Tests run: 1,  Failures: 1

Why it looks unintended

  • The page presents runnable code ("In this example we will create a WhiteSpaceTokenizer ...") and prints each step's output; there is no disclaimer.
  • Each break is a deliberate API change that left the calls stale: LUCENE-1987/2372 (3.x), LUCENE-4963, LUCENE-5859, LUCENE-6651 (its MIGRATE note requires reflectWith).
  • No Version value makes the removed constructors exist, and nobody can subclass a final class.
The full argument274 words
  • The page presents working code. "In this example we will create a WhiteSpaceTokenizer and use a LengthFilter to suppress all words that have only two or fewer characters" (package-info.java:597-598); "In this easy example a simple white space tokenization is performed. ... Here is the output:" (:647-649); "Now let's take a look at how the LengthFilter is implemented:" (:689). No "pseudo-code" or "illustrative" disclaimer anywhere on the page.
  • Deliberate API changes that left the example calls stale. LUCENE-5859 (9938a39a872, 2014, "Remove Version from Analyzer constructors") did not touch it. LUCENE-4963 (8a7f2b6cc45, 2013) edited the page's listings but left both LengthFilter(true, ...) calls. LUCENE-6651 (4accc254774, 2015) made reflectWith abstract; its MIGRATE entry: "If you have implemented your own attribute, make sure to add the required method sigature [sic]. See the Javadocs for an example."
  • No later edit fixed the stale calls. LUCENE-5392 (2b822c846a8, 2014-01-10) rewrote the three whitespace calls to new WhitespaceTokenizer(matchVersion), which matched trunk's API at the time, before LUCENE-5859 removed that constructor; later commits such as LUCENE-7318 (2016, the page's StandardAnalyzer link), #36 (2021, an enum modifier in the custom-attribute example) and LUCENE-9570 (reformatting) left the stale calls as they were; the last three (#14199, #14734, #15573, the last on 2026-01-15) are typo, grammar and markup fixes.
  • Precedent. Upstream fixes stale javadoc examples: LUCENE-9046 (TermInSetQuery) and #16385 (per its CHANGES entry, the grouping package info) were merged; #16821 and #16823 (open issues) and #16904 (an open PR) report the same kind of defect in other package docs; LUCENE-8488 (#9534, open) asks for javadoc examples to be compiled.
The devil's-advocate check132 words

The strongest case: these are illustrative fragments without imports; Version.LUCENE_XY is an explicit placeholder that tells the reader to adapt the code; and in 2012, on #4740 (LUCENE-3666), Uwe Schindler (in an IRC log posted there) advised leaving reflectWith out to keep the attribute example minimal ("too much information", "a minimum example would be ideal").

Why it fails: a reader adds missing imports and the code compiles, but no import and no Version value makes the removed constructors exist, and a final class cannot be subclassed. reflectWith was optional only while AttributeImpl had a reflection-based default; LUCENE-6651, also by Uwe Schindler, removed it, and its MIGRATE note requires the method. The page prints the output its code should produce. All verifiers and the adjudication rejected the intended-behaviour case. Verdict: a documentation defect.

Suggested fix

Recommended A. Rewrite the seven examples against the current API (javadoc only)

Rewrite the seven examples against today's API: drop Version; new StandardAnalyzer(), new WhitespaceTokenizer(), new LengthFilter(source, 3, Integer.MAX_VALUE); a small Analyzer for the gap example; super(in); add reflectWith; make the tagging filter final. Validated: the fixed page compiles, runs with -ea and prints its outputs; format, lint, javadoc and the core analysis tests pass.

All options and trade-offs3 options · 1,371 words

One option was validated in a scratch worktree of 645d4fe14a20: A, a javadoc-only rewrite of the one file. B is a variant for the position-gap example; C lists optional text cleanups.

A. Rewrite the seven examples against the current API (javadoc only) (validated by the checks) (recommended)

  • Drop every Version matchVersion = Version.LUCENE_XY line and the MyAnalyzer(Version) field and constructor.
  • new StandardAnalyzer() (#1); new WhitespaceTokenizer() (#3, #4, #7); new LengthFilter(source, 3, Integer.MAX_VALUE) (#4, #7).
  • #2: StandardAnalyzer is final, so a small Analyzer with StandardTokenizer and LowerCaseFilter (the chain StandardAnalyzer() builds; its stop set is empty) that overrides getPositionIncrementGap, as the text above the example says.
  • #5: LengthFilter(TokenStream in, int min, int max) { super(in); ... } without the @param version line; the FilteringTokenFilter listing's constructor becomes FilteringTokenFilter(TokenStream in).
  • #6: add reflectWith(AttributeReflector) and name it in the sentence after the example.
  • Make PartOfSpeechTaggingFilter final, and drop the stray new from the four printed outputs.
diff --git a/lucene/core/src/java/org/apache/lucene/analysis/package-info.java b/lucene/core/src/java/org/apache/lucene/analysis/package-info.java
index 4dfe81038f4..28a52a3abcd 100644
--- a/lucene/core/src/java/org/apache/lucene/analysis/package-info.java
+++ b/lucene/core/src/java/org/apache/lucene/analysis/package-info.java
@@ -179,8 +179,7 @@
  * something like:
  *
  * <pre id="analysis-workflow"><code class="language-java">
- *     Version matchVersion = Version.LUCENE_XY; // Substitute desired Lucene version for XY
- *     Analyzer analyzer = new StandardAnalyzer(matchVersion); // or any other analyzer
+ *     Analyzer analyzer = new StandardAnalyzer(); // or any other analyzer
  *     TokenStream ts = analyzer.tokenStream("myfield", new StringReader("some text goes here"));
  *     // The Analyzer class will construct the Tokenizer, TokenFilter(s), and CharFilter(s),
  *     //   and pass the resulting Reader to the Tokenizer.
@@ -263,8 +262,14 @@
  * Analyzer.getPositionIncrementGap(fieldName)}:
  *
  * <pre><code class="language-java">
- *   Version matchVersion = Version.LUCENE_XY; // Substitute desired Lucene version for XY
- *   Analyzer myAnalyzer = new StandardAnalyzer(matchVersion) {
+ *   Analyzer myAnalyzer = new Analyzer() {
+ *     {@literal @Override}
+ *     protected TokenStreamComponents createComponents(String fieldName) {
+ *       Tokenizer source = new StandardTokenizer();
+ *       return new TokenStreamComponents(source, new LowerCaseFilter(source));
+ *     }
+ *
+ *     {@literal @Override}
  *     public int getPositionIncrementGap(String fieldName) {
  *       return 10;
  *     }
@@ -606,23 +611,16 @@
  * <pre><code class="language-java">
  * public class MyAnalyzer extends Analyzer {
  *
- *   private Version matchVersion;
- *
- *   public MyAnalyzer(Version matchVersion) {
- *     this.matchVersion = matchVersion;
- *   }
- *
  *   {@literal @Override}
  *   protected TokenStreamComponents createComponents(String fieldName) {
- *     return new TokenStreamComponents(new WhitespaceTokenizer(matchVersion));
+ *     return new TokenStreamComponents(new WhitespaceTokenizer());
  *   }
  *
  *   public static void main(String[] args) throws IOException {
  *     // text to tokenize
  *     final String text = "This is a demo of the TokenStream API";
  *
- *     Version matchVersion = Version.LUCENE_XY; // Substitute desired Lucene version for XY
- *     MyAnalyzer analyzer = new MyAnalyzer(matchVersion);
+ *     MyAnalyzer analyzer = new MyAnalyzer();
  *     TokenStream stream = analyzer.tokenStream("field", new StringReader(text));
  *
  *     // get the CharTermAttribute from the TokenStream
@@ -655,7 +653,6 @@
  * demo
  * of
  * the
- * new
  * TokenStream
  * API
  * </pre>
@@ -669,8 +666,8 @@
  * <pre><code class="language-java">
  *   {@literal @Override}
  *   protected TokenStreamComponents createComponents(String fieldName) {
- *     final Tokenizer source = new WhitespaceTokenizer(matchVersion);
- *     TokenStream result = new LengthFilter(true, source, 3, Integer.MAX_VALUE);
+ *     final Tokenizer source = new WhitespaceTokenizer();
+ *     TokenStream result = new LengthFilter(source, 3, Integer.MAX_VALUE);
  *     return new TokenStreamComponents(source, result);
  *   }
  * </code></pre>
@@ -681,7 +678,6 @@
  * This
  * demo
  * the
- * new
  * TokenStream
  * API
  * </pre>
@@ -700,13 +696,12 @@
  *    * Create a new LengthFilter. This will filter out tokens whose
  *    * CharTermAttribute is either too short
  *    * (&lt; min) or too long (&gt; max).
- *    * {@literal @param} version the Lucene match version
  *    * {@literal @param} in      the TokenStream to consume
  *    * {@literal @param} min     the minimum length
  *    * {@literal @param} max     the maximum length
  *    *&#47;
- *  public LengthFilter(Version version, TokenStream in, int min, int max) {
- *     super(version, in);
+ *   public LengthFilter(TokenStream in, int min, int max) {
+ *     super(in);
  *     this.min = min;
  *     this.max = max;
  *   }
@@ -743,7 +738,7 @@
  *    * Create a new FilteringTokenFilter.
  *    * {@literal @param} in      the TokenStream to consume
  *    *&#47;
- *   public FilteringTokenFilter(Version version, TokenStream in) {
+ *   public FilteringTokenFilter(TokenStream in) {
  *     super(in);
  *   }
  *
@@ -828,18 +823,23 @@
  *   public void copyTo(AttributeImpl target) {
  *     ((PartOfSpeechAttribute) target).setPartOfSpeech(pos);
  *   }
+ *
+ *   {@literal @Override}
+ *   public void reflectWith(AttributeReflector reflector) {
+ *     reflector.reflect(PartOfSpeechAttribute.class, "partOfSpeech", pos);
+ *   }
  * }
  * </code></pre>
  *
  * <p>This is a simple Attribute implementation has only a single variable that stores the
  * part-of-speech of a token. It extends the <code>AttributeImpl</code> class and therefore
- * implements its abstract methods <code>clear()</code> and <code>copyTo()</code>. Now we need a
- * TokenFilter that can set this new PartOfSpeechAttribute for each token. In this example we show a
- * very naive filter that tags every word with a leading upper-case letter as a 'Noun' and all other
- * words as 'Unknown'.
+ * implements its abstract methods <code>clear()</code>, <code>copyTo()</code> and <code>
+ * reflectWith()</code>. Now we need a TokenFilter that can set this new PartOfSpeechAttribute for
+ * each token. In this example we show a very naive filter that tags every word with a leading
+ * upper-case letter as a 'Noun' and all other words as 'Unknown'.
  *
  * <pre><code class="language-java">
- *   public static class PartOfSpeechTaggingFilter extends TokenFilter {
+ *   public static final class PartOfSpeechTaggingFilter extends TokenFilter {
  *     PartOfSpeechAttribute posAtt = addAttribute(PartOfSpeechAttribute.class);
  *     CharTermAttribute termAtt = addAttribute(CharTermAttribute.class);
  *
@@ -873,8 +873,8 @@
  * <pre><code class="language-java">
  *   {@literal @Override}
  *   protected TokenStreamComponents createComponents(String fieldName) {
- *     final Tokenizer source = new WhitespaceTokenizer(matchVersion);
- *     TokenStream result = new LengthFilter(true, source, 3, Integer.MAX_VALUE);
+ *     final Tokenizer source = new WhitespaceTokenizer();
+ *     TokenStream result = new LengthFilter(source, 3, Integer.MAX_VALUE);
  *     result = new PartOfSpeechTaggingFilter(result);
  *     return new TokenStreamComponents(source, result);
  *   }
@@ -886,7 +886,6 @@
  * This
  * demo
  * the
- * new
  * TokenStream
  * API
  * </pre>
@@ -931,7 +930,6 @@
  * This: Noun
  * demo: Unknown
  * the: Unknown
- * new: Unknown
  * TokenStream: Noun
  * API: Noun
  * </pre>

Trade-offs

  • Javadoc only, so no Lucene code changes.
  • The gap example no longer shows StandardAnalyzer itself. With the default settings its index-time tokens are the same as StandardAnalyzer()'s, but it is not the same class: no setMaxTokenLength (both default to 255), no StopFilter (the default stop set is empty) and no normalize override, which matters only for query-time normalization. Replies on LUCENE-3055 (#4128) by Robert Muir and Uwe Schindler recommend writing your own Analyzer instead of subclassing StandardAnalyzer.

What the checks validated

  • Page text (a checker builds nine units from eleven of the page's Java blocks, taken verbatim and assembled as the page describes; it adds imports, packages, a main for the two fragments, the attribute's own files, the tagging filter nested in MyAnalyzer, and gap and token prints for the gap unit; it compiles each unit with javac against the 645d4fe14a20 jars, runs the six with a main under -ea and compares four outputs with the page): stock page 8 of 9 units fail to compile; fixed page 9 of 9 compile, the six run, and all four printed outputs match exactly (This is a demo of the TokenStream API; This demo the TokenStream API twice; This: Noun demo: Unknown the: Unknown TokenStream: Noun API: Noun); the gap analyzer returns 10. Without final, the two part-of-speech units throw AssertionError: TokenStream implementation classes or at least their incrementToken() implementation must be final.
  • Build: :lucene:core:checkGoogleJavaFormat, ecjLintMain and renderJavadoc pass (the fixed source has no matchVersion left); ./gradlew :lucene:core:test --tests "org.apache.lucene.analysis.*" with seed DEADBEEF passes (168 tests, 0 failures). The repro's fixed-lines variant passes with -ea and -da.

B. Keep StandardAnalyzer in the gap example with an AnalyzerWrapper (validated by the checks)

AnalyzerWrapper.getPositionIncrementGap delegates to the wrapped analyzer (AnalyzerWrapper.java:130-131); overriding it in the wrapper gives 10 (the devil's advocate ran this).

Analyzer standard = new StandardAnalyzer();
Analyzer myAnalyzer = new AnalyzerWrapper(Analyzer.PER_FIELD_REUSE_STRATEGY) {
  @Override protected Analyzer getWrappedAnalyzer(String fieldName) { return standard; }
  @Override public int getPositionIncrementGap(String fieldName) { return 10; }
};

Trade-offs

Keeps the exact StandardAnalyzer chain, but adds a second concept (wrapping, a reuse strategy) to a section about overriding one method; A matches the text better.

What the checks validated

Compiled and run by the devil's advocate: "control L266 gap=10".

C. Optional text cleanups (not run)

  • "The LengthFilter is part of the Lucene core" (:598): it is in analysis-common.
  • The FilteringTokenFilter listing (:737-775) lacks the real class's end()/skippedPositions handling: a filter built on it reports end() position increment 0 where the real LengthFilter reports 1 (input "demo of", min length 3; the devil's advocate ran this). Show the real end() or point to the source.
  • The "could begin" hint FirstTokenOfSentenceAttributeImpl (:948-968) could mention reflectWith too.

Trade-offs

Outside the compile claim; each is a one-line text change.

What the checks validated

Not run (a text change; the devil's advocate ran the end-position check only).

How it was found

Non-formal: a sweep compiled every Java code block in Lucene's non-sandbox javadoc (186 blocks in 107 files) against the main jars and flagged six blocks of this page; a targeted test reproduced them. The independent reproducer, the devil's advocate and a cross-model reviewer each found the seventh, which the sweep's pattern skipped.

The method in full107 words

Method: non-formal (a compile check of documentation code). On 2026-10-28 the hunt unit for javadoc package and overview examples (siblings of #16904) compiled all 186 <pre><code class="language-java"> blocks of the 107 non-sandbox lucene/**/src/java files that have one against the stock main a9779ef3c193 jars with the JDK compiler, and flagged six blocks of the analysis package overview with API-level errors. A targeted public-API JUnit then compiled them verbatim (6 of 7 tests fail). The seventh example, a <pre id="analysis-workflow"> block the sweep's pattern skips, was found independently by the reproducer, the devil's advocate and the intended-lens cross-model reviewer. The verifiers' tests were later cut to the one-method repro.

Prior reports, history and checks

Prior reports

No Lucene report: 183 queries over GitHub issues and PRs (incl. the JIRA archive), the mailing lists, downstream projects, Stack Overflow and the web (2026-10-29), rechecked on 2026-11-01. A 2016 Stack Overflow question hit example 1 and never reached the tracker.

Searches3 searches

Closest items, none a Lucene report of this defect: Stack Overflow 40052828 (2016, one of the seven examples); #4740 (LUCENE-3666, closed 2012, an earlier rewrite of this page, before every breaking change); #9534 (LUCENE-8488, open, compile javadoc examples, names no file); #16821, #16823, #16904, #16936 (the same kind of defect in other package docs); LUCENE-3055 (#4128, open, objects to final analyzers); lucenenet#1522 (the Lucene.NET port of this page, other project). Limits: keyword search only; GitHub search does not index PR diffs.

What was searched, when, and what turned up
WhereWhenHowFound
GitHub issues and PRs (apache/lucene, incl. the migrated JIRA archive)2026-10-2932 searches, open and closed (StandardAnalyzer(matchVersion), PartOfSpeechAttributeImpl, LengthFilter, reflectWith, package-info, LUCENE-5859 and others); file lists of 124 of the 300 newest open PRs; the 100 most recently updated closed PRs; the file's commits on main, branch_11x, branch_10_6, branch_10xno report; closest #4740, #9534, and the sibling package-doc items
JIRA, mailing lists, downstream, Stack Overflow, web2026-10-2913 JIRA queries; 43 list queries (dev@, java-user@, issues@, commits@); 7 downstream; 9 Stack Overflow; 10 webStack Overflow 40052828 (one example, 2016); nothing on the Lucene tracker or lists
Upstream recheck2026-11-01the file's commits on main (latest still 378ca8d1bbf, #15573, 2026-01-15); the changed files of the 60 newest PRs (#16873 to #16959); search "PartOfSpeechAttributeImpl"nothing new; only #4740 and the unrelated #9616

History

No single commit: five API changes broke the examples one by one (LUCENE-1987 and LUCENE-2372 in 3.0/3.1, LUCENE-4963 in 4.4, LUCENE-5859 in 5.0, LUCENE-6651 in 6.0), and none updated the calls. All seven are broken in every release from 6.0.0 to 10.6.0 and main; not fixed upstream.

Versions tested6 rows
  • 3.0.0, example 2: LUCENE-1987 (1ae5f89cfb3, 2009-10-21) removed StandardAnalyzer(), which the gap example subclassed; LUCENE-2372 (d02cbfe3c16, 2010) made the class final in 3.1, so the 2012 Version form of the example (branch_3x 1cfd251f02b, shipped in 3.6.0; trunk e825015535e, first in 4.0.0) never compiled in a release that shipped it.
  • 4.4.0, examples 4 and 7: LUCENE-4963 (8a7f2b6cc45, 2013-05-04) replaced LengthFilter(boolean, TokenStream, int, int); it edited the page and left both calls.
  • 5.0.0, examples 1, 3 and 5: LUCENE-5859 (9938a39a872, 2014-08-08; branch_5x 55594bad9b0). The primary change: it also broke the WhitespaceTokenizer line of 4 and 7. That new WhitespaceTokenizer(matchVersion) text came from a 2014 trunk docs update (LUCENE-5392, 2b822c846a8) that matched trunk's API at the time; it reached 5.0.0 in the same backport as the removal, so it never compiled in a release.
  • 6.0.0, example 6: LUCENE-6651 (4accc254774, 2015-07-02).
  • No later commit fixed the stale calls (e.g. LUCENE-7318 in 2016, #36 in 2021, LUCENE-9570 reformatting); the last three are typo, grammar and markup changes, the latest 378ca8d1bbf (#15573, 2026-01-15).
  • The release ranges below count only today's seven stale calls. Older page texts had other errors, long fixed: the 3.0.0-3.5.x workflow example called the removed new StandardAnalyzer(), and the 4.0.0 part-of-speech chain called LengthFilter(source, 3, ...), which 4.0.0 lacks.
Versions and branches executed, with results
Version or commitVerdictResult
main 645d4fe14a20 affectedone-test repro fails with assertions on and off; the page check: 8 of 9 units fail to compile, 9 of 9 with fix A
6.0.0, 7.0.0, 8.11.4, 9.0.0, 9.12.3, 10.0.0, 10.5.2, the 10.6 branch head affectedall seven examples fail; every control compiles (history probe; 8.11.4's jars with main's page text, as the 8.9-8.11 page text was not read); verifiers' JUnit on 10.5.2 and 9.12.3
5.0.0, 5.5.5 six of seventhe custom attribute still compiles (reflectWith had a default)
4.4.0, 4.10.4 three of seventhe gap example and both LengthFilter(true, ...) calls fail
3.0.0 to 4.3.1 (executed 3.0.0, 3.0.3, 3.1.0, 3.6.2, 4.0.0, 4.1.0, 4.3.1) first affected 3.0.0counting only today's seven stale calls, only the gap example fails (older page texts had other errors there, long fixed)
2.9.4 not affectedcounting only today's seven stale calls: the gap example of the time compiles; the other six did not exist in their current form

Independent checks

Confirmed, high confidence: the finder, an independent reproducer, an intended-behaviour devil's advocate and the adjudication reproduced it; a cross-model reviewer reading the code rated it real; the other cross-model review was not valid (it could not run code). A credibility audit confirmed it.

Checks and verdicts283 words

Everyone agrees on the cause: deliberate API changes that left the example calls stale. All runs used JDK 25 on Linux aarch64.

Each independent check and its verdict
CheckVerdictWhat it found
finder reproduced (six examples)The compile sweep flagged six blocks; its JUnit fails 6 of 7 on main (-ea, -da), 10.5.2 and 9.12.3.
cross-model review, refute lens (OpenAI GPT-5.6, reading only) not a valid reviewIt could not write or run anything, so its "not real" is procedural; its reading evidence (8 items) supports real.
independent reproducer realOwn 15-test JUnit (7 examples verbatim, 8 corrected controls): 7 of 15 fail on main (-ea, -da), 10.5.2 and 9.12.3; found example 1.
cross-model review, intended lens (OpenAI GPT-5.6, reading only) real, mediumThe intended-behaviour argument is weak (working code, printed output, no disclaimer); also found example 1. A separate re-run of the finder's test and a javac check of the code backed it.
intended-behaviour devil's advocate realRejected the fragment, placeholder and "minimal example" arguments; a corrected tutorial compiles and runs, and needs a final tagging filter under -ea.
adjudication confirmed, highWidened the claim to seven examples; re-ran the independent reproduction's test with a new seed: 7 of 15 fail, -ea and -da.
Follow-up, registration check, credibility audit reproducesHistory probe on 35 runs from 2.9.4 to main; no prior report found; re-runs 7 of 15 and 6 of 7.
minimal repro and this doc reproduced; fix validatedThe one-method test fails on main 645d4fe14a20 with -ea and -da; fix A compiles, runs and passes format, lint, javadoc and the core analysis tests.

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,488 words

Finding record

LFV-989, snapshot of 2026-11-06

Registry title
The org.apache.lucene.analysis package overview (the TokenStream API tutorial) has seven code examples that do not compile on any release since 6.0: they call StandardAnalyzer(Version), WhitespaceTokenizer(Version) and FilteringTokenFilter(Version, TokenStream), removed in 5.0, and LengthFilter(boolean, TokenStream, int, int), gone since 4.4, subclass the final StandardAnalyzer, and define a custom AttributeImpl without reflectWith, abstract since 6.0 (documentation defect)
Area
core / analysis (org.apache.lucene.analysis package overview, package-info.java: the TokenStream API tutorial)
Priority
Low, rank 1237: The core analysis tutorial's seven examples fail loudly at compile time with no runtime effect, and it is the most-read of the pages with broken examples.
Impact class
minor
Impact note
Documentation only, with no runtime effect: anyone who learns the TokenStream API from the core analysis package overview and copies one of its examples (the analysis workflow, the position-gap analyzer, the whitespace analyzer, adding a LengthFilter, the LengthFilter listing, the custom part-of-speech attribute, the final part-of-speech chain) gets compile errors and has to rewrite it against the current constructors; no configuration, scale or special input is involved, and all seven examples have been broken together in every release since 6.0.0 (the oldest one since 3.0.0).
Summary
You open the org.apache.lucene.analysis package overview, Lucene's tutorial for writing analyzers, token filters and attributes, and copy one of its examples, expecting it to compile as shown (the page even prints the output the code should produce). Seven examples do not compile on main or on any release since 6.0.0: they call StandardAnalyzer(Version), WhitespaceTokenizer(Version) and FilteringTokenFilter(Version, TokenStream), all removed in 5.0, and new LengthFilter(true, source, 3, Integer.MAX_VALUE), a signature gone since 4.4; one subclasses StandardAnalyzer, which is final; and the custom PartOfSpeechAttributeImpl lacks reflectWith(AttributeReflector), which has been abstract since 6.0. The APIs changed on purpose and the page was never updated: written against the current API (no Version argument, LengthFilter(source, 3, Integer.MAX_VALUE), a custom Analyzer or AnalyzerWrapper instead of a StandardAnalyzer subclass, and a reflectWith method) every example compiles.
Introduced
No single commit: the seven examples went stale at five API changes, and only one of them (LUCENE-4963) edited this page, leaving its stale calls in place [read: git log -S and git show --stat on the page under its three historical paths and on the API classes, in the full-history clone; exec: release boundary runs, see affected_versions]. Oldest: 1ae5f89cfb3 (LUCENE-1987 = #3062, uschindler, 2009-10-21, first in 3.0.0) removed StandardAnalyzer(), which the position-gap example then subclassed, and d02cbfe3c16 (LUCENE-2372 = #3448, uschindler, 2010-04-10; branch_3x merge 379c5fd00d4, 2010-05-04; 3.1) made StandardAnalyzer final, so the 2012 Version form of that example (e825015535e, sarowe, 3.6.0) never compiled in any release that shipped it. 8a7f2b6cc45 (LUCENE-4963 = #6027, jpountz, 2013-05-04; branch_4x ddf2a47e3ef; 4.4.0) replaced LengthFilter(boolean, TokenStream, int, int) with Version-first constructors and rewrote this page's LengthFilter and FilteringTokenFilter listings, but left both LengthFilter(true, ...) calls. Primary: 9938a39a872 (LUCENE-5859 = #6921, rjernst, 2014-08-08; branch_5x backport 55594bad9b0, LUCENE-5944, rmuir, 2014-09-18; 5.0.0) removed StandardAnalyzer(Version), WhitespaceTokenizer(Version) and FilteringTokenFilter(Version, TokenStream) without touching the page, breaking five of the seven groups. 4accc254774 (LUCENE-6651 = #7709, uschindler, 2015-07-02; 6.0.0) made AttributeImpl.reflectWith abstract, breaking the custom-attribute example (its MIGRATE entry tells custom attributes to implement it). (2009-10-21 (1ae5f89cfb3, the oldest break: the position-gap example); 2010-04-10 (d02cbfe3c16, StandardAnalyzer final); 2013-05-04 (8a7f2b6cc45, the LengthFilter calls); 2014-08-08 (9938a39a872, primary: the Version constructors, five groups); 2015-07-02 (4accc254774, the custom attribute); author dates)
First affected version
3.0.0 [exec] for the position-gap example (its text of the time compiles on 2.9.4 and fails on 3.0.0); 4.4.0 [exec] for the two LengthFilter(true, ...) calls; 5.0.0 [exec] for the Version constructors (five groups); 6.0.0 [exec] for the custom attribute, so every release from 6.0.0 on has all seven examples broken
Affected versions
every release from 3.0.0 to 10.5.2, 10.6.0 (released 2026-10-29; branch_10_6 4ae946e5a9b) and main a9779ef3c193; not fixed upstream. 3.0.0-4.3.1: the position-gap example only; 4.4.0-4.10.4: the gap example and both LengthFilter calls; 5.0.0-5.5.x: six of the seven (all but the custom attribute); 6.0.0 to 10.5.2, 10.6.0 and main: all seven. Not affected: 2.2.0-2.9.x (the gap example compiles on 2.9.4; the other six did not exist in their current form). [exec: the follow-up's history probe compiles each release's own published page text against that release's jars, each stale example with a control that changes only the stale call and compiles, on 2.9.4, 3.0.0, 3.0.3, 3.1.0, 3.6.2, 4.0.0, 4.1.0, 4.3.1, 4.4.0, 4.10.4, 5.0.0, 5.5.5, 6.0.0, 7.0.0, 8.11.4, 9.0.0, 9.12.3, 10.0.0, 10.5.2, the 10.6 branch head (core and analysis-common jars only) and main (-ea and -da); the finder's JUnit fails 6 of 7 and the independent reproduction's 7 of 15 on main (-ea and -da), 10.5.2 and 9.12.3] [read: the releases in between carry the same page text (per-tag text hashes) and the same constructors at the boundaries (javap); the 8.9-8.11 page text was not read (those tags are not in the history clone; the 8.11.4 jars were executed with the main text); upstream main 645d4fe14a2, branch_11x and branch_10_6 still carry the same example text]. 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 could only read the code (its low-confidence “not real” is procedural), 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, 183 recorded queries): 32 GitHub issue and PR searches (open and closed, apache/lucene, incl. the migrated JIRA archive), the file lists of 124 of the 300 newest open PRs (incl. every PR opened in the last 48 h), the 100 most recently updated closed PRs, the file's commit history on main, branch_11x, branch_10_6 and branch_10x, 13 JIRA queries, 43 mailing-list queries (dev@, java-user@, issues@, commits@), downstream projects (7 queries), Stack Overflow (9) and the web (10). Closest items: Stack Overflow question 40052828 (2016-10-14), where a 6.2.1 user found that this page's new StandardAnalyzer(matchVersion) example (the analysis-workflow block, 1 of the 7 groups) does not compile and the answer explained the Version argument was removed in 5.0; it was never raised on the Lucene tracker or lists. #4740 (LUCENE-3666, closed 2012) is an earlier rewrite of this page that predates every API change that broke today's text; #9534 (LUCENE-8488, open) is a generic wish to compile javadoc examples and names no file; #16821, #16823, #16904 and #16936 report or fix the same kind of defect in other package docs (highlight, charfilter, collation); lucenenet#1522 (2026-10-28) is the Lucene.NET port of this page, with different defects in a different project; #6921, #6027 and #7709 are the API changes themselves and do not mention the page. Recheck (2026-10-29): the file's latest upstream commit on main and branch_11x is still 378ca8d1bbf (#15573, author date 2026-01-15, formatting only) and on branch_10_6 and branch_10x 0124a2e17ab (#14734 backport); no issue or PR was created after #16937 (2026-10-28); the newest PRs #16936 and #16904 touch other files; searches for "StandardAnalyzer(matchVersion)", "PartOfSpeechAttributeImpl", 'analysis package-info LengthFilter' and '"TokenStream API" tutorial javadoc' return only the items above or unrelated ones; tracking issue #16937 does not name this page.
Lucene commit tested
main 645d4fe14a20 (2026-11-01): the one-test repro fails with assertions on and off, standalone and as a plain JUnit run; in a scratch worktree the fixed page compiles and runs (all four printed outputs match) and passes format, lint, javadoc and the core analysis tests. Earlier, on main a9779ef3c193 (same page text): the verifiers' tests on main, 10.5.2 and 9.12.3, and a history probe that compiles each release's own page text, from 2.9.4 to the 10.6 branch head.

Lucene and upstream sources

Finding record
  • lucene/core/src/java/org/apache/lucene/analysis/package-info.java:181-183 (analysis-workflow block, new StandardAnalyzer(matchVersion)), :266-267 (anonymous StandardAnalyzer subclass), :617, :672-673, :876-877 (WhitespaceTokenizer(matchVersion), LengthFilter(true, ...)), :709 (super(version, in)), :809 (PartOfSpeechAttributeImpl without reflectWith), :836 (text naming only clear() and copyTo() as abstract), :842 (non-final PartOfSpeechTaggingFilter); identical at Lucene main ea57360912ed
  • lucene/core/src/java/org/apache/lucene/analysis/standard/StandardAnalyzer.java:34, :46, :51, :61 (public final class; constructors (CharArraySet), (), (Reader)); lucene/analysis/common/src/java/org/apache/lucene/analysis/core/WhitespaceTokenizer.java:34-66 (no Version constructor); lucene/analysis/common/src/java/org/apache/lucene/analysis/miscellaneous/LengthFilter.java:44 (only (TokenStream, int, int)); lucene/core/src/java/org/apache/lucene/analysis/FilteringTokenFilter.java:38 (only (TokenStream)); lucene/core/src/java/org/apache/lucene/util/AttributeImpl.java:93 (abstract reflectWith)
The finding in 30 seconds
  • a Lucene main checkout (645d4fe14a20) lucene/core/src/java/org/apache/lucene/analysis/package-info.java:181-203, 265-272, 597-599, 606-661, 669-721, 737-775, 808-842, 873-937
  • a Lucene main checkout (645d4fe14a20) lucene/core/src/java/org/apache/lucene/analysis/standard/StandardAnalyzer.java:34, 46, 51, 61; lucene/analysis/common/src/java/org/apache/lucene/analysis/core/WhitespaceTokenizer.java:34-66; lucene/analysis/common/src/java/org/apache/lucene/analysis/miscellaneous/LengthFilter.java:44; lucene/core/src/java/org/apache/lucene/analysis/FilteringTokenFilter.java:38; lucene/core/src/java/org/apache/lucene/util/AttributeImpl.java:34, 93, 99; lucene/core/src/java/org/apache/lucene/analysis/TokenStream.java:68, 74-76, 107-112
Who is affected
  • a Lucene main checkout (645d4fe14a20) lucene/core/src/java/org/apache/lucene/analysis/TokenStream.java:68, 74-76
Why it looks unintended
  • a Lucene main checkout (645d4fe14a20) lucene/core/src/java/org/apache/lucene/analysis/package-info.java:597-599, 647-649, 678, 689
  • GitHub, read 2026-11-01: #16821 and #16823 open issues, #16904 open PR, #16385 merged 2026-08-12
Suggested fix
  • a Lucene main checkout (645d4fe14a20) lucene/core/src/java/org/apache/lucene/analysis/standard/StandardAnalyzer.java:51-53, 85-96; lucene/core/src/java/org/apache/lucene/analysis/AnalyzerWrapper.java:130-131

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

Repro files

About the diagrams

Made in parallel with this doc from the code at 645d4fe14a20 and executed probe runs on main.

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.