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).
- The lucene-monitor module matches documents against a set of stored queries: you
registerMonitorQueryobjects with aMonitor, then callmatchwith documents. ConcurrentQueryLoaderis a helper that registers many queries on several threads, for fasterMonitorstartup: youadd()each query to it, andclose()normally waits until all are registered.
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.- You load your stored queries into a
Monitorat startup and copy the six-line "Use as follows:" example from theConcurrentQueryLoaderjavadoc:List<QueryError> errors = new ArrayList<>();, thentry (ConcurrentQueryLoader loader = new ConcurrentQueryLoader(monitor, errors)) { for (MonitorQuery mq : getQueries()) { loader.add(mq); } }. - You expect it to compile and load the queries on several threads, with any load errors collected in
errors. - Instead javac stops at the first line:
cannot find symbol: class QueryError. Give it aQueryErrorof 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). - Both lines are Luwak API that the 2019 port removed on purpose, and the example was not updated: today the loader keeps the
IOExceptions thatMonitor.registerthrows, andclose()rethrows them as suppressed exceptions of oneIOException. Written asnew ConcurrentQueryLoader(monitor)without the list, it compiles; that is the form Lucene's own test uses.
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.
| line | text | exists? |
|---|---|---|
| 40 | "Use as follows:" | |
| 42 | <pre><code class="language-java"> | |
| 43 | List<QueryError> errors = new ArrayList<>(); | no: Lucene has no QueryError type |
| 44 | try (ConcurrentQueryLoader loader = new ConcurrentQueryLoader(monitor, errors)) { | no: there is no two-argument constructor |
| 45-48 | for (MonitorQuery mq : getQueries()) { loader.add(mq); } | yes, add(MonitorQuery) (line 99) |
- Constructors:
ConcurrentQueryLoader(Monitor monitor)(line 70), which uses one thread per available processor and a queue ofDEFAULT_QUEUE_SIZE = 2000(lines 63, 71), andConcurrentQueryLoader(Monitor monitor, int threads, int queueSize)(line 81). Nothing else. - Errors: a private
List<IOException> errors(line 61). A worker adds anIOExceptionthrown bymonitor.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 newIOExceptionwith every collected one as a suppressed exception (lines 115-118). OnlyIOExceptions are collected: an unchecked exception fromregisterends its worker andclose()does not report it. - On main,
QueryErroroccurs inlucene/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 noQueryErrorand only the two constructors.
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).
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
ConcurrentQueryLoaderclass 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 byMonitor.registerin the loader threads come out ofclose()as suppressed exceptions of oneIOException(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 andclose()never reports) is a runtime defect with a different fix site.
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.
/*
* 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);
}
}
# $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.
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.
| lucene | main 645d4fe14a20 (11.0.0-SNAPSHOT); the 10.6.0 release jars give the same error |
|---|---|
| jdk | OpenJDK 25.0.3 (build 25.0.3+9-LTS) |
| os | Linux aarch64 (a compile-only check: the result depends on the API in the jars, not on the platform) |
# 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- The block says "Use as follows:", is tagged as Java code, and nothing marks it as pseudo-code.
- The 2019 port removed
QueryErrorand 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 aQueryErrorclass 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") deletedQueryError.java, replaced the constructors with(Monitor)and(Monitor, int, int), madeclose()throw the collectedIOExceptions and removed the@param errorslines, 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.mdandCHANGES.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.
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<QueryError> errors = new ArrayList<>();
- * 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.
Javadoc only, lucene/monitor/src/java/org/apache/lucene/monitor/ConcurrentQueryLoader.java:43-44.
* <pre><code class="language-java">
- * List<QueryError> errors = new ArrayList<>();
- * 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 ownTestConcurrentQueryLoader). ./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:checkGoogleJavaFormatand:lucene:monitor:renderJavadocpass, and the rendered class page showsnew ConcurrentQueryLoader(monitor)and noQueryError.
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.
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.
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.
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.
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
645d4fe14a20jars and gotcannot find symbol: class QueryErroron 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.
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).
| Where | When | How | Found |
|---|---|---|---|
| Scripted and direct GitHub searches | 2026-10-29 | 93 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-wide | 0 apache/lucene items for ConcurrentQueryLoader, QueryError or MonitorQueryParser; only the port (#9811) and generic context |
| Open PRs and recent activity | 2026-10-29 | the changed files of all 415 open PRs; every item created since 2026-10-26; the item streams of this week's javadoc-fix authors | no PR touches ConcurrentQueryLoader.java; nothing mentions the monitor module |
| Re-check | 2026-11-01 | issue 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_6 | 0 each; no open PR touches lucene/monitor (incl. #16949); lines 43-44 unchanged |
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 withQueryErrorand the list constructors, so the example matched;d58aa35f5b8(2019-04-19) removed both and madeclose()throw the collectedIOExceptions, and left the example unchanged. - Merged:
251dbe7cea8on trunk andb8773d13ad8on branch_8x (released in 8.2.0), "LUCENE-8766: Add monitor subproject", Alan Woodward, 2019-06-13.git blameon main still attributes lines 43-48 to251dbe7cea8. - Later edits that kept it:
2cbf261032d(LUCENE-9570 reformat, 2021-01-05, rewrapped the prose around the block) and378ca8d1bbf3(#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.
| Version or commit | Verdict | Result |
|---|---|---|
main 645d4fe14a20; upstream main f815e679bf6c, branch_11x, branch_10_6 | affected | fails with cannot find symbol: class QueryError, -ea and -da (upstream branches read, not run: lines 43-44 unchanged) |
| 10.6.0 | affected | fails 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 | affected | fails with the same error |
| 8.2.0 | first release | fails with the same error; with IOException for QueryError, no (Monitor, List) constructor; new ConcurrentQueryLoader(monitor) compiles |
| 8.1.1 and older | no such module | not affected: the monitor module does not exist there |
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.
| Check | Verdict | What 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, high | Own 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 | real | Own 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, high | Intended 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), Low | Re-ran on main with new seeds; the one-test repro fails and its fixed twin passes. |
| Brief | verified; fix validated | Minimal 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). |
The diagram as a still image, generated with AI and checked against the code. It is under 10 MB, the limit for a GitHub attachment.
- PNG 392 KB
Finding record, Lucene and upstream sources, repro files and diagram notes1,362 words
- 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 plainjavac/javacommand); 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 of645d4fe14a20(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:checkGoogleJavaFormatand:lucene:monitor:renderJavadocpass). Upstream mainf815e679bf6c(2026-11-01), branch_11x and branch_10_6 were read, not run: lines 43-44 are unchanged.
- 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-151a 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-49a 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
- TestConcurrentQueryLoaderJavadocExample.java (one test, public API):
repro/LFV-1034/TestConcurrentQueryLoaderJavadocExample.java(67 lines)
Every value drawn is asserted against snapshots of the code at 645d4fe14a20, javap, javac outputs and the executed runs before rendering.
assets/LFV-1034/bug.png(392 KB)
