All findings · Lucene finding · LFV-1034 · concurrentqueryloader-javadoc-example-uses-nonexistent-queryerror-api

ConcurrentQueryLoader's javadoc example uses QueryError and a (Monitor, List) constructor that no Lucene release has had

The only usage example in the ConcurrentQueryLoader javadoc declares List<QueryError> errors and calls new ConcurrentQueryLoader(monitor, errors), but no Lucene release has had that type or that constructor; both are Luwak leftovers from the 2019 port. Copied as shown, it does not compile on any release from 8.2.0 to 10.6.0.

Summary

Priority
Low, rank 1255The only example for a niche lucene-monitor startup helper fails to compile with no runtime effect.
Impact
minor
Since
8.2.0 (the monitor module's first release, 2019)introduced 2019-06-13 · about 7 years
Affected
8.2.0 to 10.6.0, and main (documentation only)
Status
documented
Found by
a compile check of javadoc code examples (non-formal)

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

The example sits in a <pre><code> block of the class javadoc. Javadoc does not compile such blocks, so when the port removed the error list and QueryError, nothing failed, and Lucene's own test already used new ConcurrentQueryLoader(monitor).

Background

  1. The lucene-monitor module matches documents against a set of stored queries: you register MonitorQuery objects with a Monitor, then call match with documents.
  2. ConcurrentQueryLoader is a helper that registers many queries on several threads, for faster Monitor startup: you add() each query to it, and close() normally waits until all are registered.
Diagram of the bug. The ConcurrentQueryLoader class javadoc example, six lines (ConcurrentQueryLoader.java:43-48), beside the class's real public API from javap: line 43 declares List<QueryError> errors, but no QueryError type exists in any Lucene jar; line 44 calls new ConcurrentQueryLoader(monitor, errors), but the only constructors are (Monitor) at line 70 and (Monitor, int, int) at line 81; the loop with loader.add(mq) is fine (add(MonitorQuery), line 99). Expected: the example compiles and load errors land in the caller's errors list. Actual: javac 'cannot find symbol: class QueryError'; with QueryError replaced, 'no suitable constructor found for ConcurrentQueryLoader(Monitor,List<Exception>)'. In the real API, IOExceptions thrown by Monitor.register in the loader threads come out of close() as suppressed exceptions of one IOException. History: Luwak had both; on the 2019 port branch d58aa35f5b8 removed them and kept the example; it shipped in 8.2.0 and is identical up to 10.6.0 and main. Fix: new ConcurrentQueryLoader(monitor) without the list; it compiles, and it is the form Lucene's own TestConcurrentQueryLoader uses.
The javadoc example next to the class's real API: the loop exists, but QueryError and the two-argument constructor do not; a commit on the 2019 port branch removed them and kept the example. Select the still to open it at full size. Generated with AI, checked against the code.

The finding, step by step

  1. You load your stored queries into a Monitor at startup and copy the six-line "Use as follows:" example from the ConcurrentQueryLoader javadoc: List<QueryError> errors = new ArrayList<>();, then try (ConcurrentQueryLoader loader = new ConcurrentQueryLoader(monitor, errors)) { for (MonitorQuery mq : getQueries()) { loader.add(mq); } }.
  2. You expect it to compile and load the queries on several threads, with any load errors collected in errors.
  3. Instead javac stops at the first line: cannot find symbol: class QueryError. Give it a QueryError of your own and it stops at the second: no suitable constructor found for ConcurrentQueryLoader(Monitor,List<...>). The class has only 2 constructors, (Monitor) and (Monitor, int, int).
  4. Both lines are Luwak API that the 2019 port removed on purpose, and the example was not updated: today the loader keeps the IOExceptions that Monitor.register throws, and close() rethrows them as suppressed exceptions of one IOException. Written as new ConcurrentQueryLoader(monitor) without the list, it compiles; that is the form Lucene's own test uses.

Who is affected

Users of the lucene-monitor module who bulk-load stored queries with the opt-in ConcurrentQueryLoader and start from its only example: one compile error, every time, on 8.2.0 to 10.6.0 and main. Documentation only: nothing runs differently, and the constructor javadoc shows the working form.

Mechanism, terms and numbers548 words

ConcurrentQueryLoader (lucene-monitor) registers many queries with a Monitor on several threads, "to speed up startup times for a Monitor". Its class javadoc says "Use as follows:" and shows six lines: List<QueryError> errors = new ArrayList<>();, then try (ConcurrentQueryLoader loader = new ConcurrentQueryLoader(monitor, errors)) { for (MonitorQuery mq : getQueries()) { loader.add(mq); } }. Lucene has no QueryError type, and the class has only the constructors (Monitor) and (Monitor, int, int). Copied into a project with monitor and getQueries() supplied, the example fails with cannot find symbol: class QueryError; with QueryError replaced by a real type, javac reports no suitable constructor found for ConcurrentQueryLoader(Monitor,List<...>). Load errors are not collected in a caller's list either: the loader keeps the IOExceptions that Monitor.register throws in its threads, and close() normally rethrows them as suppressed exceptions of one IOException (unchecked exceptions are not collected). The two lines are the API of Luwak, the project the module was ported from; a commit on the port branch removed QueryError and the list constructors in April 2019 and left the example as it was. Written as new ConcurrentQueryLoader(monitor) without the list, the example compiles, and that is the form Lucene's own TestConcurrentQueryLoader uses. It has been broken in every release that has the module, 8.2.0 to 10.6.0, and on main. Nothing in Lucene behaves wrongly; only the example is stale.

The example (main 645d4fe14a20, ConcurrentQueryLoader.java)

linetextexists?
40"Use as follows:"
42<pre><code class="language-java">
43List<QueryError> errors = new ArrayList<>();no: Lucene has no QueryError type
44try (ConcurrentQueryLoader loader = new ConcurrentQueryLoader(monitor, errors)) {no: there is no two-argument constructor
45-48for (MonitorQuery mq : getQueries()) { loader.add(mq); }yes, add(MonitorQuery) (line 99)

The real API

  • Constructors: ConcurrentQueryLoader(Monitor monitor) (line 70), which uses one thread per available processor and a queue of DEFAULT_QUEUE_SIZE = 2000 (lines 63, 71), and ConcurrentQueryLoader(Monitor monitor, int threads, int queueSize) (line 81). Nothing else.
  • Errors: a private List<IOException> errors (line 61). A worker adds an IOException thrown by monitor.register(...) (lines 141, 144-145); close() normally waits for the workers (if the closing thread is interrupted, it stops waiting, lines 110-114) and then throws one new IOException with every collected one as a suppressed exception (lines 115-118). Only IOExceptions are collected: an unchecked exception from register ends its worker and close() does not report it.
  • On main, QueryError occurs in lucene/ only on line 43 and in an unrelated test method name (TestHighlightingMatcher.testQueryErrors); no commit on any ref of the Lucene history ever added such a class (only the never-merged port PR's branch, in the author's fork, had one). A bytecode scan of every build with the class (8.2.0 to 10.5.2, and main) finds no QueryError and only the two constructors.

Why nothing caught it

Javadoc does not compile <pre> blocks, and the example has no {@link} that javadoc would check. Lucene's only test of the class, TestConcurrentQueryLoader.testLoading, uses new ConcurrentQueryLoader(monitor) and loads 200 queries (2000 nightly).

Not claimed

Any runtime defect; the free example names monitor and getQueries() (normal for a fragment). Side observation: line 51, "The Monitor's MonitorQueryParser must be thread-safe for this to work correctly", names a MonitorQueryParser type that Lucene does not have either (a Luwak interface); a fix may reword it.

Exposure and likelihood197 words
  • Who. Readers of the ConcurrentQueryLoader class javadoc who copy its example. No query, document or index file reaches it.
  • Default vs opt-in. The loader is an opt-in helper; nothing in Lucene uses it by default. No configuration or special input is involved: the copied example always fails.
  • Scale. Every release that has the monitor module, 8.2.0 to 10.6.0 (the six lines are identical in all 49 release tags with the file and in the sources jars of the untagged 8.x releases), and main.
  • Effect. A compile error, not wrong behaviour. The example also implies that load errors land in a list the caller passes in; in the real API, IOExceptions thrown by Monitor.register in the loader threads come out of close() as suppressed exceptions of one IOException (on the normal path), so a reader may look for them in the wrong place.
  • Priority. Low; the same kind of defect as other stale javadoc examples (for example LFV-1029, LFV-984, LFV-1018 to LFV-1021, LFV-1026).
  • Separate bug in the same class. LFV-513 (workers exit at the first exception from Monitor.register; add() can then block forever and close() never reports) is a runtime defect with a different fix site.

Minimal repro

One JUnit class with one test. It compiles the six example lines, copied verbatim, with javax.tools against the jars on the test class path; it adds only the imports, an enclosing class, a Monitor monitor parameter and a getQueries() method. Nothing in Lucene runs, and nothing is random.

TestConcurrentQueryLoaderJavadocExample.java (one test, public API)67 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.monitor;

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 TestConcurrentQueryLoaderJavadocExample extends LuceneTestCase {

  // Expected: the class-javadoc example compiles; actual: javac says cannot find symbol class QueryError.
  public void testClassJavadocExampleCompiles() throws Exception {
    String source =
        """
        import java.util.ArrayList;
        import java.util.List;
        import org.apache.lucene.monitor.*;
        abstract class Example {
          abstract List<MonitorQuery> getQueries();
          void example(Monitor monitor) throws Exception {
            List<QueryError> errors = new ArrayList<>();
            try (ConcurrentQueryLoader loader = new ConcurrentQueryLoader(monitor, errors)) {
                for (MonitorQuery mq : getQueries()) {
                    loader.add(mq);
                }
            }
          }
        }
        """;
    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
# $LUCENE: a built checkout (./gradlew jar); $REPRO: the directory holding org/apache/lucene/monitor/Test...java
# $DEPS: junit 4.13.2, randomizedtesting-runner 2.9.2, hamcrest 3.0
V=11.0.0-SNAPSHOT
L=$LUCENE/lucene
CP=$L/core/build/libs/lucene-core-$V.jar:$L/codecs/build/libs/lucene-codecs-$V.jar:$L/test-framework/build/libs/lucene-test-framework-$V.jar:$L/monitor/build/libs/lucene-monitor-$V.jar:$DEPS
javac -d out -cp "$CP" $REPRO/org/apache/lucene/monitor/TestConcurrentQueryLoaderJavadocExample.java
java -ea -Dtests.seed=DEADBEEF -cp "out:$CP" org.junit.runner.JUnitCore org.apache.lucene.monitor.TestConcurrentQueryLoaderJavadocExample
# assertions off, same result: java -da -Dtests.asserts=false ...
Expected
The copied example compiles.
Observed
One javac error, cannot find symbol: class QueryError, with assertions on and off, on main and on the 10.6.0 jars.

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

More files, environment, full output250 words

The block in the test was checked mechanically against ConcurrentQueryLoader.java:43-48 at 645d4fe14a20 (HTML entities decoded): it is verbatim.

/Example.java:7 in the output is line 7 of the compiled snippet, which is line 43 of ConcurrentQueryLoader.java. javac stops at the missing type; with QueryError replaced by an existing type, the next error is no suitable constructor found for ConcurrentQueryLoader(Monitor,List<Exception>), listing only ConcurrentQueryLoader(Monitor) and ConcurrentQueryLoader(Monitor,int,int) (a separate variant run, not part of this test).

No separate regression test is suggested: this test copies the example, so it would not notice a later edit of the javadoc, and Lucene has no harness that compiles javadoc examples (LUCENE-8488, #9534). Lucene's TestConcurrentQueryLoader.testLoading already runs the corrected form.

Environment

lucenemain 645d4fe14a20 (11.0.0-SNAPSHOT); the 10.6.0 release jars give the same error
jdkOpenJDK 25.0.3 (build 25.0.3+9-LTS)
osLinux aarch64 (a compile-only check: the result depends on the API in the jars, not on the platform)

Observed output

# the command above, main 645d4fe14a20, seed DEADBEEF, -ea (-da identical)
There was 1 failure:
1) testClassJavadocExampleCompiles(org.apache.lucene.monitor.TestConcurrentQueryLoaderJavadocExample)
java.lang.AssertionError: [/Example.java:7: error: cannot find symbol
    List<QueryError> errors = new ArrayList<>();
         ^
  symbol:   class QueryError
  location: class Example]
	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.monitor.TestConcurrentQueryLoaderJavadocExample.testClassJavadocExampleCompiles(TestConcurrentQueryLoaderJavadocExample.java:65)
	...
FAILURES!!!
Tests run: 1,  Failures: 1

# the same test on the 10.6.0 release jars, -da: tests_run=1 failures=1, the same single diagnostic

# variant (QueryError replaced by an existing type), not part of the test:
Example.java:8: error: no suitable constructor found for ConcurrentQueryLoader(Monitor,List<Exception>)
    constructor ConcurrentQueryLoader.ConcurrentQueryLoader(Monitor) is not applicable
    constructor ConcurrentQueryLoader.ConcurrentQueryLoader(Monitor,int,int) is not applicable

Why it looks unintended

  • The block says "Use as follows:", is tagged as Java code, and nothing marks it as pseudo-code.
  • The 2019 port removed QueryError and the list constructors on purpose and left the example as it was; no record says it was kept on purpose.
  • Lucene's own test uses new ConcurrentQueryLoader(monitor).
The full argument198 words
  • Offered as working code. The example is the class's only usage sample, under "Use as follows:" and, since #15573 (2026-01-15), in <code class="language-java">.
  • History of the port. In Luwak, ConcurrentQueryLoader(Monitor monitor, List<QueryError> errors) and a QueryError class existed, with this same example. On the never-merged port branch (apache/lucene-solr#648) the "Initial commit" 266d913136e (2019-04-14) imported them, so the example matched. d58aa35f5b8 (2019-04-19, "Refactor the Monitor to take Query objects rather than string representations") deleted QueryError.java, replaced the constructors with (Monitor) and (Monitor, int, int), made close() throw the collected IOExceptions and removed the @param errors lines, but did not touch the example. On #9811 (LUCENE-8766) the author, Alan Woodward, wrote "I've spent a bit of time cleaning up the API and reducing its surface area" (2019-04-16).
  • The checked records do not accept the old form. package-info.java, MIGRATE.md and CHANGES.txt (the three places checked) say nothing about the loader's error API or about the example being illustrative.
  • Precedent. A stale class-javadoc example was fixed upstream as an ordinary doc fix before: issue #12666 (the Analyzer "Simple example") was closed by PR #12693, merged by msokolov on 2023-10-18. LUCENE-8488 (#9534) asks for compile-checked examples in general.
The devil's-advocate check183 words

Strongest case for "intended" or "noise": Lucene does not compile-check javadoc examples (#9534 is open), and in #15225 (2025-10-25) rmuir wrote that "Code examples need to be kept extremely minimal in the rare cases where it does make sense: the maintenance cost is very high and we don't have great validation for these." The block already leaves monitor and getQueries() undeclared, so QueryError could be read as a placeholder; the failure is loud, and the constructor javadoc and the test show the real usage. The cross-model reviews rated this "weak" as an intended-behaviour argument.

Why it fails: a reader supplies context such as monitor, but no context makes a constructor that does not exist compile; even with a QueryError of the reader's own, new ConcurrentQueryLoader(monitor, errors) fails. The block is prescriptive and is the class's only example, and the history shows the example left unchanged during a deliberate API change. rmuir's remark argues for few, minimal examples: it supports fixing or deleting this one, not keeping it broken. Every verifier and the adjudication called it real; the adjudication kept it at Low.

Suggested fix

Recommended Drop the list and use the one-argument constructor (validated)

Drop the List<QueryError> line and write new ConcurrentQueryLoader(monitor) (javadoc only: 1 line removed, 1 shortened). Validated: the patched example compiles on main and on 10.6.0, and the lucene-monitor module's tests pass (181 tests, 0 failures).

  * <pre><code class="language-java">
- *     List&lt;QueryError&gt; errors = new ArrayList&lt;&gt;();
- *     try (ConcurrentQueryLoader loader = new ConcurrentQueryLoader(monitor, errors)) {
+ *     try (ConcurrentQueryLoader loader = new ConcurrentQueryLoader(monitor)) {
  *         for (MonitorQuery mq : getQueries()) {
  *             loader.add(mq);
  *         }
All options and trade-offs5 options ยท 405 words

One fix was validated in a scratch worktree of 645d4fe14a20; the alternatives were not run.

Drop the list and use the one-argument constructor (validated) (recommended)

Javadoc only, lucene/monitor/src/java/org/apache/lucene/monitor/ConcurrentQueryLoader.java:43-44.

  * <pre><code class="language-java">
- *     List&lt;QueryError&gt; errors = new ArrayList&lt;&gt;();
- *     try (ConcurrentQueryLoader loader = new ConcurrentQueryLoader(monitor, errors)) {
+ *     try (ConcurrentQueryLoader loader = new ConcurrentQueryLoader(monitor)) {
  *         for (MonitorQuery mq : getQueries()) {
  *             loader.add(mq);
  *         }

Trade-offs

None for users: documentation only, and no CHANGES entry is strictly needed. The error behaviour stays undocumented in the class javadoc, as it is today.

What the checks validated

  • The block extracted from the patched file (lines 43-44 are the only changed lines), compiled by the same one-test check: passes 1 of 1 on main with assertions on and off and on the 10.6.0 jars. The stock block fails the same test, so the test detects the bug.
  • The corrected loop loads queries: a control registered 5 of 5 queries with assertions on and off (it used the (Monitor, 2, 4) constructor; the one-argument form is run by Lucene's own TestConcurrentQueryLoader).
  • ./gradlew :lucene:monitor:test --tests "org.apache.lucene.monitor.TestConcurrentQueryLoader" -Ptests.seed=DEADBEEF (the closest suite, which runs the corrected form): 1 test, 0 failures; the whole module, ./gradlew :lucene:monitor:test -Ptests.seed=DEADBEEF: 181 tests, 0 failures, 1 skipped. ./gradlew :lucene:monitor:checkGoogleJavaFormat and :lucene:monitor:renderJavadoc pass, and the rendered class page shows new ConcurrentQueryLoader(monitor) and no QueryError.

Also say how load errors are reported (optional, not validated)

For example: "IOExceptions thrown by Monitor#register in the loader threads are rethrown by close() as suppressed exceptions of a single IOException."

Trade-offs

Word it narrowly: only IOExceptions are collected (an unchecked exception from register is not reported), and by LFV-513 add() can block before close() is reached. A broader wording ("If registering queries fails, close() throws ...") would overclaim, as the verifiers noted. If LFV-513's fix changes error reporting, the sentence must follow.

Reword the MonitorQueryParser sentence (optional, outside the claim)

Line 51 names a MonitorQueryParser that Lucene does not have (a Luwak interface).

Trade-offs

Not claimed and not executed; check what thread-safety requirement, if any, still applies before rewording.

Delete the example instead

In the spirit of rmuir's #15225 remark that code examples are costly to maintain.

Trade-offs

The class loses its only usage sample; the constructor javadoc remains.

Follow-up: compile-check javadoc examples

The generic wish of LUCENE-8488 (#9534, open since 2018).

Trade-offs

Would stop this class of drift, but it is a build-infrastructure change, not part of this fix.

How it was found

No formal method. A probe compiled the code examples of the monitor and neighbouring modules' javadocs against the main jars and hit this error; the hunt then extracted the block mechanically, checked in 158 builds which API members exist, and wrote the one-test repro. Independent verifiers reproduced it with their own tests.

The method in full122 words
  • First signal. A probe compiled join, memory, monitor, suggest and facet javadoc examples against the stock main 645d4fe14a20 jars and got cannot find symbol: class QueryError on this block (2026-10-29).
  • Hunt. A hunt unit for javadoc examples re-ran the lead, extracted the block mechanically, and ran a bytecode scan over 158 builds (1.9.1 to main) for every API member the example uses, with boundaries cross-checked by javac. The scan finds the class from 8.2.0 on, never a QueryError, and only the two real constructors.
  • Earlier note. A previous hunt unit (2026-10-26) had mentioned the stale example in passing, without running it, while working on a different bug in the same class (LFV-513).
  • Tier. Non-formal: it compiles documentation code and scans jars.

Prior reports, history and checks

Prior reports

No prior report: GitHub issues and PRs (with the migrated JIRA archive), commits, the dev@, java-user@, issues@ and commits@ lists, downstream repositories and the web were searched on 2026-10-29; re-checked 2026-11-01. ConcurrentQueryLoader, QueryError and MonitorQueryParser match no apache/lucene issue or PR; no open PR touches the module.

Searches3 searches

Related, not reports: #9811 (LUCENE-8766, the port; its 27 comments never mention this class or its javadoc); #9534 (LUCENE-8488, a generic wish to compile examples); #15573 (retagged this block and kept the stale lines); #16922 (the same kind of defect in another class, an outside report); #15225 (an umbrella issue on javadoc quality); Stack Overflow 66678363 (quotes Luwak's README, not this javadoc).

Others file javadoc fixes almost daily (#16904, #16922, #16949).

What was searched, when, and what turned up
WhereWhenHowFound
Scripted and direct GitHub searches2026-10-2993 queries (class, type and constructor names, error text, javadoc phrasing) over GitHub issues, PRs and commits, downstream repositories, dev@, java-user@, issues@, commits@, the JIRA archive and the web; 17 direct GitHub searches, apache/lucene and GitHub-wide0 apache/lucene items for ConcurrentQueryLoader, QueryError or MonitorQueryParser; only the port (#9811) and generic context
Open PRs and recent activity2026-10-29the changed files of all 415 open PRs; every item created since 2026-10-26; the item streams of this week's javadoc-fix authorsno PR touches ConcurrentQueryLoader.java; nothing mentions the monitor module
Re-check2026-11-01issue and PR search for ConcurrentQueryLoader, QueryError, MonitorQueryParser; the changed files of all 31 open PRs updated since 2026-10-29; lines 43-44 on main, branch_11x and branch_10_60 each; no open PR touches lucene/monitor (incl. #16949); lines 43-44 unchanged

History

Stale since the module first shipped: a commit on the port branch removed QueryError and the list constructors on 2019-04-19 and kept the example; the squashed port 251dbe7cea8 (trunk) and b8773d13ad8 (branch_8x, 2019-06-13) shipped it in 8.2.0. 8.1.1 and older have no monitor module.

Versions tested5 rows
  • On the port branch (apache/lucene-solr#648, never merged): 266d913136e (2019-04-14) imported the Luwak class with QueryError and the list constructors, so the example matched; d58aa35f5b8 (2019-04-19) removed both and made close() throw the collected IOExceptions, and left the example unchanged.
  • Merged: 251dbe7cea8 on trunk and b8773d13ad8 on branch_8x (released in 8.2.0), "LUCENE-8766: Add monitor subproject", Alan Woodward, 2019-06-13. git blame on main still attributes lines 43-48 to 251dbe7cea8.
  • Later edits that kept it: 2cbf261032d (LUCENE-9570 reformat, 2021-01-05, rewrapped the prose around the block) and 378ca8d1bbf3 (#15573, 2026-01-15, the <pre> markup; the newest commit on the file).
  • Not a regression: no release and no merged Lucene commit ever had a compiling version of this example; it matched the API only on the never-merged port branch, before d58aa35f5b8.
Versions and branches executed, with results
Version or commitVerdictResult
main 645d4fe14a20; upstream main f815e679bf6c, branch_11x, branch_10_6 affectedfails with cannot find symbol: class QueryError, -ea and -da (upstream branches read, not run: lines 43-44 unchanged)
10.6.0 affectedfails with the same error (release jars, -da)
10.5.2, 10.0.0, 9.12.3, 9.1.0, 9.0.0, 8.11.4 affectedfails with the same error
8.2.0 first releasefails with the same error; with IOException for QueryError, no (Monitor, List) constructor; new ConcurrentQueryLoader(monitor) compiles
8.1.1 and older no such modulenot affected: the monitor module does not exist there

Independent checks

A finder, an independent reproducer, a reviewer from another model family (OpenAI GPT-5.6) with lenses, a devil's-advocate check, an adjudication and an audit. All reproduced it or saw it reproduced; the adjudication confirmed it with high confidence as a Low documentation defect.

Checks and verdicts246 words

The refute-lens review executed code; the intended-lens review could only read and a separate run re-ran the test for it. Both ran on an older checkout where the file is identical to 645d4fe14a20.

Each independent check and its verdict
CheckVerdictWhat it found
Finder reproduced (doc)Bytecode scan of 158 builds plus the compile check: fails on main (-ea, -da), 10.5.2 and 9.12.3; the fix check passes; a control of the corrected loop loads 5 of 5 queries.
Refute lens (OpenAI GPT-5.6) real, highOwn claim test fails (two seeds, -ea, -da, 10.5.2, 9.12.3); the one-argument control passes; a stub QueryError isolates the missing constructor; Lucene's TestConcurrentQueryLoader passes.
Reproducer realOwn test fails on main (-ea, -da), 10.5.2 and 9.12.3; controls pass 2 of 2 (the corrected block compiles; with IOException for QueryError, javac lists only the two real constructors).
Intended lens (OpenAI GPT-5.6) real, highIntended argument weak; read only; separate re-runs fail on main (-ea, -da) and 10.5.2.
Devil's advocate real (Low doc defect)Re-ran on main (-ea, -da) and 10.5.2; argued the "illustrative fragment" case and rejected it; asked to narrow the fix's close() sentence.
Adjudication and audit confirmed (high), LowRe-ran on main with new seeds; the one-test repro fails and its fixed twin passes.
Brief verified; fix validatedMinimal test fails on main (-ea, -da) and on 10.6.0; the patched block passes; the lucene-monitor module's tests pass (181 tests, 0 failures).

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

Finding record

LFV-1034, snapshot of 2026-11-06

Registry title
ConcurrentQueryLoader's class javadoc example declares List<QueryError> and calls new ConcurrentQueryLoader(monitor, errors), but no QueryError type and no (Monitor, List) constructor has ever existed in Lucene, so the example has not compiled on any release since the monitor module was added in 8.2.0 (documentation defect)
Area
monitor / ConcurrentQueryLoader (class javadoc example)
Priority
Low, rank 1255: The only example for a niche lucene-monitor startup helper fails to compile with no runtime effect.
Impact class
minor
Impact note
Documentation only, with no runtime effect: a developer who bulk-loads queries into a lucene-monitor Monitor with ConcurrentQueryLoader (the class's stated use: faster Monitor startup) and copies the class's only code example gets a compile error on every release since 8.2.0, and the example suggests that load errors land in a caller-supplied list, while the code rethrows IOExceptions from close(); the constructor javadoc and the module's own test show the working form new ConcurrentQueryLoader(monitor), and no configuration, scale or special input is involved.
Summary
The ConcurrentQueryLoader javadoc says 'Use as follows:' and shows a loop that first creates a List<QueryError> errors and passes it to new ConcurrentQueryLoader(monitor, errors). Copied into code, it does not compile on any release: Lucene has no QueryError class, and the only constructors are (Monitor) and (Monitor, int, int); the two lines are left over from Luwak, the project the monitor module was ported from, whose API had both. Written as new ConcurrentQueryLoader(monitor) without the list, the example compiles (the same form the module's own test uses), so only the javadoc is wrong.
Introduced
251dbe7cea8 (trunk) and its branch_8x copy b8773d13ad8, both 'LUCENE-8766: Add monitor subproject' (migrated as #9811, 'Add Luwak as a lucene module'), by Alan Woodward (romseygeek), author date 2019-06-13; the branch_8x commit is the one released in 8.2.0. They added ConcurrentQueryLoader with the class-javadoc example copied from Luwak while the class already had only the (Monitor) and (Monitor, int, int) constructors, so the example never compiled in any Lucene commit. The mismatch began on the never-merged PR branch apache/lucene-solr#648: its commit d58aa35f5b8 (2019-04-19, 'Refactor the Monitor to take Query objects rather than string representations') deleted QueryError.java and the error-list constructors and made close() throw the collected IOExceptions, but left the example unchanged; the branch was squashed into the two commits above. git blame on main 645d4fe14a20 still attributes example lines 43-48 to 251dbe7cea8; the later commits 2cbf261032d (LUCENE-9570 reformat, 2021-01-05) and 378ca8d1bbf3 (#15573, 2026-01-15, the <pre> markup) touched the block without fixing it [read: git log, show and blame in the full-history clone; exec: version runs, see affected_versions] (2019-06-13 (251dbe7cea8 / b8773d13ad8, LUCENE-8766, author date); the API change that made the copied example stale: 2019-04-19 on the never-merged PR branch (d58aa35f5b8))
First affected version
8.2.0 [exec] (the first release of the monitor module: the verbatim example fails there with 'cannot find symbol: class QueryError', and new ConcurrentQueryLoader(monitor) compiles); 8.1.1 and earlier have no monitor module [exec]
Affected versions
every lucene-monitor release from 8.2.0 to 10.6.0 (57 versions to 10.5.2, and 10.6.0 = branch_10_6 4ae946e5a9b [exec 2026-11-01: the minimal test fails on the 10.6.0 release jars, lucene-monitor-10.6.0.jar included, with the same error]), branch_10x 657014b3c579, branch_11x f85446cb1af1 and main 645d4fe14a20; not affected: 8.1.1 and earlier (no monitor module). Not fixed upstream: upstream main is still at 645d4fe14a20 and the newest commit on the file is 378ca8d1bbf3 (#15573, 2026-01-15), which kept the stale lines (2026-10-29). [exec: the finder's test fails on main (-ea and -da), 10.5.2, 9.12.3 and 9.1.0 (the earliest release it can run on); a version-neutral main() port that compiles the same verbatim lines fails on 8.2.0, 8.11.4, 9.0.0, 9.12.3, 10.0.0, 10.5.2 and main, its 'ctor' mode (QueryError replaced by IOException) fails on 8.2.0, 10.5.2 and main with 'no suitable constructor found for ConcurrentQueryLoader(Monitor, List<IOException>)', and its control new ConcurrentQueryLoader(monitor) compiles on 8.2.0, 9.12.3, 10.5.2 and main; the independent reproduction's test fails on main (-ea and -da), 10.5.2 and 9.12.3] [read: the six example lines are identical in all 48 release tags of the history clone from 8.2.0 to 10.5.2, in the lucene-monitor sources jars of 8.8.1 to 8.11.4 from Maven Central (the history clone has no tags for 8.8.2 to 8.11.4), on branch_10_6, branch_10x and main; a bytecode scan of every build from 8.2.0 to main finds no QueryError class and only the (Monitor) and (Monitor, int, int) constructors]. 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, the intended-behaviour check's and the adjudication's searches agree): scripted searches over GitHub issues, PRs and commits, downstream repositories, the dev@, java-user@, issues@ and commits@ lists, the JIRA archive and the web; 17 direct GitHub searches (apache/lucene and GitHub-wide: ConcurrentQueryLoader, QueryError and MonitorQueryParser return 0 apache/lucene items); the changed files of all 415 open apache/lucene PRs (newest #16938; none touches ConcurrentQueryLoader.java); everything created since 2026-10-26; the issue and PR streams of this week's outside javadoc-fix authors; all lucene.apache.org and solr.apache.org lists (only commit-diff mails); JIRA (nothing about this example). Read but not the same bug: #9811 (LUCENE-8766, the import that carried the example over; its 27 comments never mention this class or its javadoc), #9534 (LUCENE-8488, an open 2018 wish to compile-check javadoc examples, naming none), #15573 (a markup sweep that retagged this block and kept the stale lines), #10830 (LUCENE-9791, a different monitor bug), #14660 and #11607 (other monitor files), #16922 (the same kind of defect in UpgradeIndexMergePolicy, an outside report), #15225 (open umbrella issue on javadoc quality, no monitor mention), Stack Overflow 66678363 (quotes Luwak's README, not this javadoc). Recheck: five GitHub searches (ConcurrentQueryLoader, QueryError, MonitorQueryParser, with and without javadoc, GitHub-wide once) return 0 items and a broader 'monitor javadoc example' search returns only unrelated items, the items updated since a recent cutoff (#16940, #16939, #16907, #16623) are unrelated, none of the open PRs created since 2026-10-28 (#16929-#16938) touches lucene/monitor, and the file's newest commit on main is still 378ca8d1bbf3.
Lucene commit tested
main 645d4fe14a20 (2026-10-28): the minimal test fails with assertions on and off (standalone, with a plain javac/java command); the 10.6.0 release jars give the same error; 8.2.0, 8.11.4, 9.0.0, 9.1.0, 9.12.3, 10.0.0 and 10.5.2 were run in the version-history runs; the fix was checked in a scratch worktree of 645d4fe14a20 (the patched example compiles on main and on 10.6.0; the lucene-monitor module's tests pass (181 tests, 0 failures, 1 skipped); ./gradlew :lucene:monitor:checkGoogleJavaFormat and :lucene:monitor:renderJavadoc pass). Upstream main f815e679bf6c (2026-11-01), branch_11x and branch_10_6 were read, not run: lines 43-44 are unchanged.

Lucene and upstream sources

Finding record
  • lucene/monitor/src/java/org/apache/lucene/monitor/ConcurrentQueryLoader.java:34-52 (class javadoc, 'Use as follows:' at 40, the example at 43-48, MonitorQueryParser sentence at 51), :70 and :81 (the only constructors), :107-120 (close() throws one IOException with the collected errors as suppressed exceptions), :139-147 (Worker.run collects only IOException); lucene/monitor/src/test/org/apache/lucene/monitor/TestConcurrentQueryLoader.java:26 (new ConcurrentQueryLoader(monitor))
The finding in 30 seconds
  • a Lucene main checkout (645d4fe14a20) lucene/monitor/src/java/org/apache/lucene/monitor/ConcurrentQueryLoader.java:35-53 (class javadoc; "Use as follows:" at 40, the block at 42-49, the example lines 43-48, MonitorQueryParser at 51), :61, :63, :70-72, :81, :99, :107-120, :134-151
  • a Lucene main checkout (645d4fe14a20) lucene/monitor/src/test/org/apache/lucene/monitor/TestConcurrentQueryLoader.java:20-33 (testLoading: new ConcurrentQueryLoader(monitor) at 26; 200 queries, 2000 nightly)
  • a Lucene main checkout (645d4fe14a20) lucene/monitor/src/java/org/apache/lucene/monitor/Monitor.java:38-41, :152, :174, :240 and package-info.java:19-27 (the background: register MonitorQuery objects, then match documents)
  • a Lucene main checkout (645d4fe14a20): git grep QueryError over lucene/ (ConcurrentQueryLoader.java:43; TestHighlightingMatcher.java:113 testQueryErrors)
Why it looks unintended
  • a Lucene main checkout (645d4fe14a20) lucene/monitor/src/java/org/apache/lucene/monitor/ConcurrentQueryLoader.java:40-49
  • a Lucene main checkout (645d4fe14a20) lucene/monitor/src/test/org/apache/lucene/monitor/TestConcurrentQueryLoader.java:26
Suggested fix
  • a Lucene main checkout (645d4fe14a20) lucene/monitor/src/java/org/apache/lucene/monitor/ConcurrentQueryLoader.java:51, :141-147

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

Repro files

About the diagrams

Every value drawn is asserted against snapshots of the code at 645d4fe14a20, javap, javac outputs and the executed runs before rendering.

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.