All findings · Lucene finding · LFV-368 · gettopdims-admits-unknown-count-dims-without-hits

getTopDims returns null for a multi-valued dimension the query did not hit

With taxonomy or SortedSetDocValues facets, getTopDims returns a null entry for each multi-valued dimension without requireDimCount that the query did not hit, which can push out a dimension with hits: two documents give [null, b], or [null] for the top one, instead of [b]. Every release since 9.2.0 does this.

Summary

Priority
Medium, rank 175Under the default requireDimCount=false, getTopDims returns null or drops the only dimension with hits on every filtered query, which is deterministic and fairly routine, but major downstream projects do not use the 9.2 API.
Impact
silent wrong results
Since
9.2.0 (#747, LUCENE-10325, 2022)introduced 2022-03-28 · about 4 years
Affected
9.2.0 to 10.6.0 (10.6.0 by reading) and main
Status
documented
Found by
bounded-exhaustive testing of the facet API, confirmed by JUnit

AI-assisted page: this finding was first signalled by a semi-formal check (an exact check of the real jar, or a modelling hypothesis that a tool then witnessed) 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

getTopDims(topNDims, topNChildren) returns the top dimensions of a facet result with their top labels. Its javadoc says the result should be the same as the first topNDims entries of getAllDims, which lists only the dimensions that had hits.

Summary card. Title: getTopDims admits a multi-valued dimension that has no hits, and returns null for it. Example executed on main: dims a and b setMultiValued(dim, true), requireDimCount left false; doc 0 has a=a1, doc 1 has b=b1; the query id:1 hits doc 1 only. Inside getTopDims: dim a (0 hits) gets dimCount -1, -1 != 0 admits it, createFacetResult returns null (no child with a hit); dim b (1 hit) gets -1, is admitted, and returns b b1 (1). Expected, the first topNDims of getAllDims(10): getTopDims(10, 10) = [b], getTopDims(1, 10) = [b]. Actual on main: getTopDims(10, 10) = [null, b] (a null element); getTopDims(1, 10) = [null] with SortedSetDocValues facets (b is dropped); with -ea an AssertionError in getTopDims (:230; TaxonomyFacets :686). Footnote: with taxonomy facets, getTopDims(1, 10) is [null] only when the b document is indexed first; in the order shown, taxonomy visits b first and the separate stale-dimOrd bug (LFV-363) returns a's name over b1 (1), with no assertion. Affected: SortedSetDocValuesFacetCounts and its concurrent variant, FastTaxonomyFacetCounts, the taxonomy association facets. Fix: admit a -1 dim only if it has a child with a hit.
Expected [b] twice; actual on main [null, b] and [null]. Taxonomy top-1 is [null] only with b indexed first. Select the still to open it at full size. Generated with AI, checked against the code.
  1. Mark dimensions a and b multi-valued; requireDimCount stays false, the default. Index one document with b=b1 and then one with a=a1, and search for the b document only.
  2. You expect getTopDims(10, 10) and getTopDims(1, 10) to return [b]: the documented result is the first entries of getAllDims(10), which is [b].
  3. You get [null, b] and [null]. The dimension without hits takes a slot, and with one slot the only dimension that has a hit is gone. With assertions enabled, Lucene's own assert facetResult != null fails instead.
  4. Why: without requireDimCount the dimension's count is unknown, so getTopDims gives it the placeholder -1 without looking for hits. The check meant to skip dimensions without hits only skips 0, and the empty dimension's result is null.
  5. Taxonomy counts, the int and float association facets, SortedSetDocValues facets and their concurrent variant all do this; getAllDims is right. Admitting such a dimension only if one of its children has a hit fixes it, and the whole facet test suite passes.

Who is affected

Applications that call getTopDims on taxonomy or SortedSetDocValues facets with a dimension marked multi-valued and no requireDimCount, which association dimensions cannot set. A query that misses such a dimension gets a null entry whenever it ranks into the top topNDims, and can lose a dimension with hits when topNDims is small. Deleting the documents does not help in a taxonomy.

Mechanism, terms and numbers778 words

Facets#getTopDims(topNDims, topNChildren) is documented to return the same as the first topNDims entries of getAllDims, which lists only the dimensions that had hits. Taxonomy facets and SortedSetDocValues facets override it with a faster version. For a dimension marked setMultiValued(dim, true) and left at requireDimCount = false (the default, and the only option for association dimensions), the override cannot know the dimension's count and uses the placeholder -1, without checking whether any of the dimension's labels was hit. The admission check value != 0 lets -1 through; a dimension without hits has no result, so the list gets a null entry. Two documents are enough (b=b1 indexed before a=a1): getAllDims(10) = [b], but getTopDims(10, 10) = [null, b] and getTopDims(1, 10) = [null], which drops the only dimension with a hit; with -ea, Lucene's own assert facetResult != null throws. It affects FastTaxonomyFacetCounts, TaxonomyFacetIntAssociations, TaxonomyFacetFloatAssociations, SortedSetDocValuesFacetCounts and ConcurrentSortedSetDocValuesFacetCounts, in every release from 9.2.0 (SortedSetDocValues) or 9.3.0 (taxonomy) to 10.6.0 (10.6.0 by reading) and main. Admitting such a dimension only if one of its children has a hit fixes it; the repro and the whole lucene/facet test suite pass with assertions on and off.

Terms

  • Dimension: a facet field such as Author or Tag; its labels (children) are the values, such as a1. getAllDims(topN) returns one FacetResult per dimension "that had hits" (Facets.java:53), sorted by value; getTopDims(topNDims, topNChildren) "should be the same as calling getAllDims and then only using the first topNDims" (Facets.java:61-62).
  • Multi-valued without requireDimCount: FacetsConfig.setMultiValued(dim, true) lets a document carry several labels of one dimension. Then the dimension's count is not the sum of its labels' counts, and Lucene only knows it if setRequireDimCount(dim, true) indexes it, "which is unusual (default is false)" (FacetsConfig.java:120-124); association dimensions cannot set it at all (FacetsConfig.java:305-307). Without it the dimension's value is reported as -1, by getAllDims too.
  • The fast getTopDims: taxonomy facets (TaxonomyFacets.java:594-690, used by FastTaxonomyFacetCounts and the int and float association facets) and SortedSetDocValues facets (AbstractSortedSetDocValueFacetCounts.java:142-235, used by SortedSetDocValuesFacetCounts and ConcurrentSortedSetDocValuesFacetCounts) rank dimensions by value in a priority queue first and compute the top labels only for the winners.

The bug

  1. The fast getTopDims looks at every dimension of the facet field in the taxonomy or reader, hit or not. For a multi-valued dimension without requireDimCount it sets the value to -1, "a place-holder", without looking at the counts of its children (TaxonomyFacets.java:629-638; AbstractSortedSetDocValueFacetCounts.java:174-183).
  2. A dimension enters the queue if its value is not 0 (TaxonomyFacets.java:652; AbstractSortedSetDocValueFacetCounts.java:200). That check was meant to skip dimensions without hits, and does so for the other kinds of dimensions, whose value is 0 then. -1 passes. All -1 dimensions tie, and the tie goes to the smaller name.
  3. For each dimension in the queue, createFacetResult builds the result; it "Will return null if there are no children" with a hit (TaxonomyFacets.java:301-310; AbstractSortedSetDocValueFacetCounts.java:362-371). The code asserts the result is not null, "since only dims with non-zero values were considered earlier", and stores it anyway (TaxonomyFacets.java:685-687; AbstractSortedSetDocValueFacetCounts.java:228-232).
  4. getAllDims builds the same results and drops the nulls (TaxonomyFacets.java:572-575; AbstractSortedSetDocValueFacetCounts.java:121-125), so the two methods disagree exactly on these dimensions.

The numbers

Dimensions a and b, both multi-valued, requireDimCount false; doc 1 has b=b1, doc 2 has a=a1; the query matches doc 1.

callexpected (first entries of getAllDims(10))assertions offassertions on
getAllDims(10)[b][b] (right)[b]
getTopDims(10, 10)[b][null, b]AssertionError inside getTopDims
getTopDims(1, 10)[b][null]: b, the only dimension with a hit, is droppedAssertionError
getTopDims(3, 10), one non-matching document with three such dimensions named !h1, !h2, !h3two dimensions with hits[null, null, null]-

The first three rows hold for FastTaxonomyFacetCounts (dense and sparse counting), TaxonomyFacetIntAssociations, SortedSetDocValuesFacetCounts and ConcurrentSortedSetDocValuesFacetCounts. The top-10 row was run for all of them on main with assertions on and off, and on 10.5.2, 9.12.3 and 9.3.0; the top-1 row by this doc's test for dense taxonomy counts and SortedSetDocValues facets, and by the fact check for the other three on main (on and off) and 10.5.2. The document order matters only for taxonomy top-1: with a indexed first, taxonomy facets visit b first and the sibling bug LFV-363 returns [a value=-1 childCount=1 / b1 (1)] instead of [null], with no assertion even with -ea. The last row comes from the follow-up's probe on main, taxonomy and SortedSetDocValues facets. With requireDimCount=true, or with single-valued dimensions, the same data gives [b].

Exposure and likelihood440 words
  • Who is exposed. Callers of Facets#getTopDims on FastTaxonomyFacetCounts, TaxonomyFacetIntAssociations, TaxonomyFacetFloatAssociations, SortedSetDocValuesFacetCounts or ConcurrentSortedSetDocValuesFacetCounts with at least one dimension marked setMultiValued(dim, true) without requireDimCount. That includes every multi-valued association dimension (requireDimCount is rejected for them) and the dimensions of an application that makes new dimensions multi-valued by default through getDefaultDimConfig, which its javadoc gives as an example ("like {@code multivalue = true}"), except SortedSetDocValues hierarchical dimensions.
  • When. Deterministically, whenever a query has no hits in at least one such dimension and that dimension ranks into the top topNDims admitted dimensions: always when topNDims is at least the number of admitted dimensions, otherwise only against other -1 dimensions, by name, because a dimension with a value above -1 ranks above it. Queries without hits in some dimensions are the "sparse faceting" case getAllDims is documented for; a filtered query rarely hits every dimension.
  • What the caller sees (production, assertions off): a null entry, so code that reads each entry's dim or labelValues throws a NullPointerException (inferred from the returned null); a dimension with hits pushed out when topNDims is smaller than the number of admitted dimensions ([null] instead of [b]), which dropping nulls does not undo; in the extreme every slot ([null, null, null]). With assertions on (tests), an AssertionError inside getTopDims.
  • Deleting does not help (taxonomy). The taxonomy is append-only, so a dimension whose documents were all deleted and merged away still gets a null entry; SortedSetDocValues facets drop it once the deletes are merged (follow-up probe on main).
  • With the sibling bug LFV-363 (taxonomy only: when an entry replaces the queue's bottom, its ordinal is not updated), the null can instead come back as the hit-less dimension's name over another dimension's labels: [m_hidden value=-1 childCount=1 {r1=1}], where r1 belongs to the dimension that had the hit. The recommended fix here removes that combined case too.
  • Default vs opt-in. getTopDims and setMultiValued are opt-in calls; requireDimCount=false is the default. Codec, IndexWriterConfig and searcher do not matter (query-time code; the tests use Lucene's randomized test settings and failed with every seed tried).
  • Not affected. getAllDims, getTopChildren, hits and drill-down; requireDimCount dimensions; single-valued dimensions (all executed controls); by the code, SortedSetDocValues hierarchical dimensions (they read the indexed dimension count) and the default Facets#getTopDims of other facet classes (a getAllDims prefix). Nothing is written to the index, so a fix needs no reindex.
  • Reach. A read-only code search of the default branches found no call to getTopDims in Elasticsearch, OpenSearch or Solr; the bug reaches applications that use the facet module directly. Other branches and private applications were not searched.

Minimal repro

The test shows that Facets#getTopDims on taxonomy (FastTaxonomyFacetCounts) and SortedSetDocValues facets admits a multi-valued dimension without requireDimCount that has no hits for the query, so its null result lands in the list.

TestGetTopDimsDimWithoutHits.java (JUnit test)82 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.facet;

import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.StringField;
import org.apache.lucene.facet.sortedset.DefaultSortedSetDocValuesReaderState;
import org.apache.lucene.facet.sortedset.SortedSetDocValuesFacetCounts;
import org.apache.lucene.facet.sortedset.SortedSetDocValuesFacetField;
import org.apache.lucene.facet.taxonomy.FastTaxonomyFacetCounts;
import org.apache.lucene.facet.taxonomy.directory.DirectoryTaxonomyReader;
import org.apache.lucene.facet.taxonomy.directory.DirectoryTaxonomyWriter;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.index.IndexableField;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.TermQuery;
import org.apache.lucene.store.ByteBuffersDirectory;
import org.apache.lucene.store.Directory;
import org.apache.lucene.tests.util.LuceneTestCase;

public class TestGetTopDimsDimWithoutHits extends LuceneTestCase {

  private static Document doc(String id, IndexableField facet) {
    Document doc = new Document();
    doc.add(new StringField("id", id, Field.Store.NO));
    doc.add(facet);
    return doc;
  }

  public void testMultiValuedDimWithoutHits() throws Exception {
    FacetsConfig config = new FacetsConfig();
    config.setMultiValued("a", true); // requireDimCount stays false (the default)
    config.setMultiValued("b", true);
    try (Directory taxoIndexDir = new ByteBuffersDirectory();
        Directory taxoDir = new ByteBuffersDirectory();
        Directory ssdvIndexDir = new ByteBuffersDirectory();
        IndexWriter taxoIndex = new IndexWriter(taxoIndexDir, new IndexWriterConfig());
        DirectoryTaxonomyWriter taxoWriter = new DirectoryTaxonomyWriter(taxoDir);
        IndexWriter ssdvIndex = new IndexWriter(ssdvIndexDir, new IndexWriterConfig())) {
      for (String dim : new String[] {"a", "b"}) {
        taxoIndex.addDocument(config.build(taxoWriter, doc(dim, new FacetField(dim, "x"))));
        ssdvIndex.addDocument(config.build(doc(dim, new SortedSetDocValuesFacetField(dim, "x"))));
      }
      TermQuery onlyB = new TermQuery(new Term("id", "b"));
      try (DirectoryReader taxoReader = DirectoryReader.open(taxoIndex);
          DirectoryTaxonomyReader taxonomy = new DirectoryTaxonomyReader(taxoWriter);
          DirectoryReader ssdvReader = DirectoryReader.open(ssdvIndex)) {
        Facets taxo =
            new FastTaxonomyFacetCounts(
                taxonomy,
                config,
                new IndexSearcher(taxoReader).search(onlyB, new FacetsCollectorManager()));
        Facets ssdv =
            new SortedSetDocValuesFacetCounts(
                new DefaultSortedSetDocValuesReaderState(ssdvReader, config),
                new IndexSearcher(ssdvReader).search(onlyB, new FacetsCollectorManager()));
        // expected: getTopDims is the getAllDims prefix [b]; actual: [null, b] and [null] (-ea: AssertionError)
        assertEquals(
            "taxonomy " + taxo.getAllDims(10) + " ssdv " + ssdv.getAllDims(10),
            "taxonomy " + taxo.getTopDims(10, 10) + " ssdv " + ssdv.getTopDims(1, 10));
      }
    }
  }
}
Command
# in a copy of this finding's repro folder (repro/LFV-368/, which holds src/)
# Lucene checkout at the tested commit, built with: ./gradlew --no-daemon assemble   (JDK 25)
LUCENE=/path/to/lucene; V=12.0.0-SNAPSHOT
CP=$LUCENE/lucene/core/build/libs/lucene-core-$V.jar
CP=$CP:$LUCENE/lucene/codecs/build/libs/lucene-codecs-$V.jar
CP=$CP:$LUCENE/lucene/test-framework/build/libs/lucene-test-framework-$V.jar
CP=$CP:$LUCENE/lucene/facet/build/libs/lucene-facet-$V.jar
CP=$CP:randomizedtesting-runner-2.9.2.jar:junit-4.13.2.jar:hamcrest-3.0.jar   # test-framework dependencies (Maven Central)
CP=$CP:junit-platform-console-standalone-6.1.3.jar   # JUnit Platform console launcher (Maven Central)

javac -d classes -cp "$CP" $(find src -name '*.java')

# run 1: assertions on, seed DEADBEEF
java -ea -esa --add-modules jdk.incubator.vector --enable-native-access=ALL-UNNAMED -Dtests.seed=DEADBEEF \
     -cp "classes:$CP" org.junit.platform.console.ConsoleLauncher \
     execute --select-class org.apache.lucene.facet.TestGetTopDimsDimWithoutHits
# the module(s) facet also need their third-party runtime jars
Expected vs actual
Expected the getAllDims(10) prefix [b] for both; actual taxonomy getTopDims(10, 10) = [null, b] and SSDV getTopDims(1, 10) = [null] with -da, and Lucene's own assert facetResult != null throws AssertionError (TaxonomyFacets.java:686) with -ea.

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

More files, environment, full output29 words

Environment

luceneLucene main e1faeba08bde (2026-11-02)
jdk25.0.3+9-LTS

Observed output

# Lucene main 645d4fe14a20, assertions on (-ea), seed DEADBEEF: 1 test(s) run, 1 failed
    => java.lang.AssertionError
       org.apache.lucene.facet.taxonomy.TaxonomyFacets.getTopDims(TaxonomyFacets.java:686)
       org.apache.lucene.facet.taxonomy.FastTaxonomyFacetCounts.getTopDims(FastTaxonomyFacetCounts.java:40)
       org.apache.lucene.facet.TestGetTopDimsDimWithoutHits.testMultiValuedDimWithoutHits(TestGetTopDimsDimWithoutHits.java:78)

Why it looks unintended

  • Javadoc: results "should be the same as calling getAllDims and then only using the first topNDims"; getAllDims lists dimensions "that had hits".
  • Lucene asserts the entry is never null, "since only dims with non-zero values were considered earlier".
  • Upstream tests assert that equality for this configuration; they pass only because every dimension in them has hits.
The full argument254 words
  • Contract. Facets.java:59-64: getTopDims results "should be the same as calling getAllDims and then only using the first topNDims"; Facets.java:52-56: getAllDims returns "topN labels for any dimension that had hits". Neither allows a null entry or a dimension without hits.
  • The code's own invariant. assert facetResult != null (TaxonomyFacets.java:686, AbstractSortedSetDocValueFacetCounts.java:230), with the comment "should not be null since only dims with non-zero values were considered earlier" (:229). The -1 value itself is supported: "we cannot compute an accurate dim count, and use -1 as a place-holder" (TaxonomyFacets.java:635-636), and getAllDims reports the same -1. Only the admission is wrong.
  • Tests. TestSortedSetDocValuesFacets#testBasic (:63-130) has dimension a multi-valued without requireDimCount (:65) and asserts getTopDims(10, 10) equals getAllDims(10), a with value -1 included (:125-130); testCombinationsOfConfig (dimension e, :203-205) asserts getTopDims(i, 10) == getAllDims(10).subList(0, i) for every i (:299-306); TestTaxonomyFacetAssociations#testIntSumAssociation (:211-240) asserts getTopDims(10, 10) equals getAllDims(10) for multi-valued association dimensions (:237-240). Every dimension in those tests has hits.
  • History. In the review of #747, which added getTopDims, the reviewer asked to keep "the existing behavior, which is that a FacetResult is not returned when there are no children seen" and to "check for a value of 0 and then skip over that dim": the != 0 check is that suggestion. Nobody noted that these dimensions carry -1, not 0, even without hits. #915 (9.3.0) added the asserts; its description says "No new tests were written".
The devil's-advocate check180 words

The strongest "intended" case: Facets is @lucene.experimental; the getTopDims javadoc says "dims should be configured as requiring dim counts if using this functionality to ensure accurate counts are available", so a multi-valued dimension without requireDimCount could be called unsupported; the contract says "should"; and the ranking among -1 dimensions is arbitrary anyway.

Why it fails: the advice is about accurate counts (the -1 value, which getAllDims reports the same way), not about null entries or listing dimensions without hits. It cannot be followed for association dimensions, where FacetsConfig throws "AssociationFacetField cannot requireDimCount", and requireDimCount is documented as "unusual (default is false)". Lucene's own tests use this exact configuration and assert the prefix equality, and the code asserts the null cannot happen. Ties are broken by name the same way in both methods, so [b] is well defined. @lucene.experimental concerns API stability, not wrong results. Verdict: a defect (adjudication: confirmed, high; every verifier rated the intended case at most moderate; the only "not real" vote was a read-only review that could not run Java and agreed on the root cause).

Suggested fix

Recommended Use the -1 placeholder only for dimensions with a child that has a hit

In both getTopDims overrides, give a multi-valued dimension without requireDimCount the placeholder -1 only if one of its children has a hit, and 0 otherwise, which the existing check already skips. The repro, the closest facet suites and the whole facet test suite (314 tests) pass with assertions on and off.

// TaxonomyFacets#getTopDims (multi-valued dim, no requireDimCount)
-              dimValue = -1;
+              dimValue = hasChildWithHits(dimOrd) ? -1 : 0;

// AbstractSortedSetDocValueFacetCounts#getTopDims
-            dimCount = -1;
+            dimCount = hasChildWithCount(ordRange.iterator()) ? -1 : 0;

// hasChildWithHits: walk the dim's children (dense counts) or the hash table
// (sparse counts) and return true at the first child with a count > 0
All options and trade-offs4 options · 782 words

The check uses the same criterion as the per-dimension child count that getAllDims relies on (children with a count > 0, for dense and hash-table counting), so for the affected -1 branch getTopDims admits exactly the dimensions getAllDims lists, with the same -1 value and order. Other kinds of dimensions keep the unchanged value != 0 check, so dimensions with hits whose aggregate is 0 are still dropped (#13191, a different problem). The top labels are still computed only for the dimensions that make the final list. It also removes the combined case with the sibling bug LFV-363 (a hit-less dimension is never admitted), but not LFV-363 itself. Only query-time code changes, so no reindex is needed. Workaround until a fix ships: take the first topNDims entries of getAllDims(topNChildren).

Use the -1 placeholder only for dimensions with a child that has a hit (validated by the checks) (recommended)

TaxonomyFacets.java:637 and AbstractSortedSetDocValueFacetCounts.java:182: replace -1 with a check that stops at the first child with a count > 0, and use 0 (skipped by the existing admission test) when there is none. +41/-4 in the two files, including two small private helpers.

// TaxonomyFacets#getTopDims (multi-valued dim, no requireDimCount)
-              dimValue = -1;
+              dimValue = hasChildWithHits(dimOrd) ? -1 : 0;

// AbstractSortedSetDocValueFacetCounts#getTopDims
-            dimCount = -1;
+            dimCount = hasChildWithCount(ordRange.iterator()) ? -1 : 0;

// hasChildWithHits: walk the dim's children (dense counts) or the hash table
// (sparse counts) and return true at the first child with a count > 0

Trade-offs

  • One extra scan per such dimension: it stops at the first child with a hit; for a dimension without hits it walks all of its children (dense counts), the whole hash table (sparse counts, as getTopChildrenForPath already does per dimension) or its whole ordinal range (SortedSetDocValues). The worst case is the number of hit-less -1 dimensions times the number of their children, or times the number of non-zero hash entries in sparse mode; a maintainer may prefer one pass that marks the dimensions with hits (not implemented). No benchmark was run.
  • The deferred top-children computation that LUCENE-10488 added is kept: it still runs only for the dimensions in the final list.

What the checks validated

  • Scratch worktree of a9779ef3c193, gradle, JDK 25: the new test fails 7 of 8 on stock (assertions on and off) and passes 8 of 8 with the fix; with TestSortedSetDocValuesFacets, TestTaxonomyFacetCounts and TestTaxonomyFacetAssociations: 4 suites, 58 tests, 1 skipped, 0 failures, on and off; the whole :lucene:facet:test, 40 suites and 314 tests, 0 failures, with assertions on (7 skipped) and off (8 skipped); :lucene:facet:check -x test (google-java-format, ecj lint, forbidden APIs) passes.
  • Standalone runner with the fix shadowed: the new test 8 of 8 (-ea, -da); the independent reproducer's 6 of 6; the history test's 13 of 13; the finder's 2 of 2; the association test's 2 of 2; the follow-up's 14-test probe 14 of 14 (7 fail on stock). A mutant with the taxonomy hash-table branch of the check forced to true fails, of the taxonomy tests, only the sparse-counting test, so that branch is covered (the 3 SortedSetDocValues tests also fail in that run because only the taxonomy source was patched in).
  • Not run: a backport to 9.x or 10.x (the same lines exist there), x86, a benchmark.

Compute such a dimension's top children up front and skip it if it has none (validated by the checks)

The finder's sketch: in the -1 branch call getTopChildrenForPath, skip the dimension when its child count is 0, and keep the result for the result phase. +23/-3 in the two files.

Trade-offs

Same results. It computes the top labels of every such dimension, including those that do not make the final list, which partly undoes the LUCENE-10488 optimisation.

What the checks validated

The new test passes 8 of 8 (standalone, -ea and -da); the whole :lucene:facet:test, 40 suites and 314 tests, passes with assertions on. Earlier, the finder's, the independent reproducer's, the history and the probe tests passed with it.

Admit every dimension if and only if it has a child with a hit

Replace the value != 0 test for all kinds of dimensions.

Trade-offs

Would also cover #13191 (dimensions with hits whose aggregate is 0 are dropped), the goal of open PR #13287, which removes the same taxonomy line. Broader behaviour change; not executed.

Workaround: the first entries of getAllDims

Call getAllDims(topNChildren) and take the first topNDims entries, which is what the base Facets#getTopDims does.

Trade-offs

Loses the optimisation of the fast getTopDims. Dropping nulls from the fast result is not enough when topNDims is small; setting requireDimCount is impossible for association dimensions.

Downstream note

Applications that share one index among users and enforce access only through the query should know that a dimension carried only by documents outside a user's view can still take null slots in that user's getTopDims result.

How it was found

An AI finder wrote an exhaustive checker for the facet result API: every subset of hits over 12 modelled documents, compared getTopDims with the first entries of getAllDims and with brute-force counts. It first hit Lucene's own assertion inside getTopDims, then null entries with assertions off, and the finder reduced it to a two-document JUnit test.

The method in full171 words

Method: bounded-exhaustive translation validation of the facet result API (the hunt's "semi-formal" tier), a hunt unit (taxonomy, SortedSetDocValues and drill-sideways facets), 2026-10-26, stock main jars.

  • The harness indexes 12 modelled documents and runs every one of the 2^12 hit subsets, with and without 1,100 padding documents (to reach hash-table counting), over several segment layouts and with deletions. For FastTaxonomyFacetCounts, SortedSetDocValuesFacetCounts and ConcurrentSortedSetDocValuesFacetCounts it checks getTopDims against the first entries of getAllDims and exact brute-force counts.
  • It first stopped at an AssertionError inside getTopDims. With assertions off, seed 1 gave 1,584 null entries (496 SortedSetDocValues, 512 concurrent, 576 taxonomy) among 3,504 mismatches in 703,437 checks; the other 1,920 mismatches are the sibling bug LFV-363's mislabelled entries.
  • With this fix and the sibling's, 184 of 192 configurations completed with 0 mismatches in 131,356,014 checks; the other 8 stopped at the harness's own setup check before checking anything.
  • The finder reduced it to a public-API JUnit test with two documents; then independent AI tools reproduced, reviewed and judged it (see the checks).

Prior reports, history and checks

Prior reports

No prior report found (best-effort search, 2026-10-26): 108 logged searches (90 tool queries, 18 manual searches and reads) over GitHub, commits, downstream projects, four mailing lists, JIRA and the web, every getTopDims issue and PR, and all 411 open PRs. Nearest: open PR #13287, which changes the same taxonomy line for a different problem and does not fix this one.

Searches5 searches

No issue, PR, review comment, JIRA entry, list thread, commit or web page reports that getTopDims admits a multi-valued dimension without requireDimCount when the query has no hits in it, or its symptoms (a null entry, the assertion, a dimension with hits pushed out). The newest upstream main fetched, a95c754ee00 (2026-10-26, facet module unchanged since a9779ef3c193), and branch_10x and branch_10_6 still have the -1 assignment, the != 0 check and the assert.

Nearest item: open, stale PR #13287 (2024-04-10, "Don't assume non-zero aggregations in getTopDims or test checkResults", for issue #13191) deletes the taxonomy admission line for the opposite defect (dimensions with hits and an aggregate of 0 dropped). It never mentions multi-valued dimensions, requireDimCount, -1, null results or SortedSetDocValues facets. Run as a shadow, it does not fix this repro (6 of 6 still fail) and makes a single-valued no-hit control return null too.

Context, not reports: #11361 / #747 / #773 (the feature), #11524 / #777 / #779 / #806 / #889 (the optimisation), #915 and #12966 (refactors), #11565 (a test NPE confirming that getTopChildren returns null for a dimension without hits), #12585 (non-positive aggregation values), #12862 (same files, getSpecificValue only). Gaps: keyword search only; GitHub search does not index diffs; one GitHub run was rate-limited (later runs completed); expired CI logs; private reports are not visible.

What was searched, when, and what turned up
WhereWhenHowFound
GitHub issues, PRs and commits (apache/lucene)2026-10-26three scripted prior-report search runs (30 issue and PR queries, 3 commit queries: getTopDims with null, NullPointerException, AssertionError, requireDimCount, multi-valued, "no hits"; code fragments such as "dimCount != 0" and "only dims with non-zero values"); all 11 issues and PRs that mention getTopDims readno report; #13287 and the context items above
Open PRs2026-10-26the changed files of all 411 open apache/lucene PRs#13287 (same admission line), #12862 (same files, getSpecificValue only), #13948, #12547, #11780 (unrelated)
Review threads and history2026-10-26the review threads of #747, #777, #779, #806, #889, #915 and #12966; git log -S over the full historythe #747 review chose the != 0 skip; nobody discussed hit-less -1 dimensions; no fix commit
Mailing lists and JIRA2026-10-2640 tool queries over dev@, java-user@, issues@ and commits@; 8 JIRA queries; LUCENE-9952 and LUCENE-10529 readno report
Downstream repositories and web2026-10-262 queries over Elasticsearch, OpenSearch and Solr; 7 web queriesno report; no downstream call to getTopDims

History

Introduced in 9.2.0 by #747 (LUCENE-10325, 2022), which added getTopDims with the SortedSetDocValues override; copied into the concurrent and taxonomy versions the same year. Taxonomy facets return the null from 9.3.0. Every release from 9.2.0 to 10.5.2 and main fails; 9.1.0 has no getTopDims. Not fixed upstream.

Versions tested6 rows

Introduced by LUCENE-10325 "Add getTopDims functionality to Facets", #747 (@Yuti-G, merged by @gsmiller, 7c33f04d374 on main, 2022-03-28; branch_9x backport #773, 77ccf2fb359), which added Facets#getTopDims with the SortedSetDocValues override and its dimCount != 0 check. Copies: ConcurrentSortedSetDocValuesFacetCounts and taxonomy facets (LUCENE-10488: #777 ef43242d77a, #779, #806; branch_9x #889 87655fd015c, 2022-05-13), all in 9.2.0. In 9.2.0 taxonomy getAllDims also listed the hit-less dimension, so the two methods agreed; LUCENE-10585 (#915, 8db1e41fc07; branch_9x 075f5103e9a, 2022-05-29, 9.3.0) made getAllDims drop it and added the assert, leaving the admission unchanged. #12966 (9ba4af7b883, 2024-04-05) moved the code into TaxonomyFacets and kept it. Single-file swaps on 9.2.0 pin each commit.

Versions and branches executed, with results
Version or commitVerdictResult
main a9779ef3c193 affectedthis doc's test: 7 of 8 fail (gradle and standalone, assertions on and off): [null, b] and [null]
10.5.2, 9.12.3, 9.3.0 affectedthis doc's test: 7 of 8 fail, the same messages (10.5.2 also with -ea: TaxonomyFacets.java:695, AbstractSortedSetDocValueFacetCounts.java:237)
9.3.0 to 10.5.2 (30 releases), main ea57360912ed release sweepthe follow-up's history test: 8 of 13 fail with -ea and -da; controls pass
9.2.0 SSDV onlySortedSetDocValues and concurrent: the null entry (no assert yet); taxonomy getAllDims also lists the hit-less dimension (value -1, no children), a related 9.2.0-only defect
9.1.0 no APIno getTopDims: the repro does not compile
main + the fix not affectedevery repro passes; the whole lucene/facet suite passes with assertions on and off

Independent checks

An independent reproducer, a devil's advocate and an adjudication found it real, each with its own executed test; a read-only review by another model family agreed, and a second one, which could not run Java, gave a procedural "not real", low. The adjudication confirmed it with high confidence; this doc's gradle runs reproduced it on main a9779ef3c193.

Checks and verdicts414 words

Every check that ran code used the stock jars, with assertions on and off. The reviews by the other model family (OpenAI GPT-5.6) ran read-only; separate runs re-ran the finder's test, which failed both times.

Each independent check and its verdict
CheckVerdictWhat it found
finder candidateExhaustive harness, then a public-API JUnit: 2 of 2 fail on main (-ea, -da) and with -da on 10.5.2, 9.12.3, 9.4.2 and 9.2.0; pass with the fix.
cross-model review, refute lens not real (low, procedural)Could not run Java, so it voted not real on procedure only; root cause agreed, inputs in contract; it judged the requireDimCount advice no excuse for a null entry. A separate re-run failed as claimed.
Independent reproducer realOwn 6-test JUnit: 5 of 6 fail on main, 10.5.2 and 9.12.3 (-ea, -da), 9.3.0 and 9.2.0 (-da); 6 of 6 pass with the fix. Found PR #13287 and showed it does not fix this; corrected the first taxonomy release to 9.3.0.
Cross-model review, intended lens real (medium)OpenAI GPT-5.6, read-only: intended argument moderate but no excuse; a separate run re-ran the repro and the fix, and a variant probe (concurrent the same; requireDimCount correct).
Devil's advocate realNo intended-behaviour case survives: the advice is about counts and impossible for association facets, upstream tests assert the prefix equality, and the code asserts the null cannot happen. Its association test fails on main.
adjudication confirmed (high)Re-ran the independent reproduction's test with a new seed: 5 of 6 fail with -ea and -da; 6 of 6 pass with the fix. Not a duplicate.
Follow-up history, prior art, securityHistory on 31 releases and main; prior-art search, verdict new; security probe on main, 10.5.2 and 9.12.3 (re-evaluated to none).
registry re-runs and audit reproduced; keepFresh seeds: the finder's test fails on main (-ea, -da) and 10.5.2 (-da) and passes with the fix (main, -ea); the independent reproduction's fails 5 of 6. An independent audit re-run kept the row.
this doc's validation reproduced, fix validatedNew test: 7 of 8 fail on main (gradle and standalone, -ea and -da), 10.5.2, 9.12.3 and 9.3.0, and on 9.2.0 (there the taxonomy tests fail one step earlier, at getAllDims); with the recommended fix it passes, and so do the whole lucene/facet suite (314 tests, on and off) and the other verifiers' 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,556 words

Finding record

LFV-368, snapshot of 2026-11-06

Registry title
Facets#getTopDims on taxonomy and SortedSetDocValues facets admits a multi-valued dimension without requireDimCount even when the query has no hits in it, so the result list contains null (AssertionError with -ea) and a dimension that has hits can be pushed out (released since 9.2.0)
Area
facet / getTopDims of taxonomy and SortedSetDocValues facets (TaxonomyFacets#getTopDims and AbstractSortedSetDocValueFacetCounts#getTopDims, shared by FastTaxonomyFacetCounts, the taxonomy int and float association facets, SortedSetDocValuesFacetCounts and ConcurrentSortedSetDocValuesFacetCounts)
Priority
Medium, rank 175: Under the default requireDimCount=false, getTopDims returns null or drops the only dimension with hits on every filtered query, which is deterministic and fairly routine, but major downstream projects do not use the 9.2 API.
Impact class
silent wrong results
Impact note
Applications that call Facets#getTopDims(topNDims, topNChildren) on FastTaxonomyFacetCounts, TaxonomyFacetIntAssociations, TaxonomyFacetFloatAssociations, SortedSetDocValuesFacetCounts or ConcurrentSortedSetDocValuesFacetCounts with a dimension marked FacetsConfig#setMultiValued and left at requireDimCount=false (the default; multi-valued association dimensions cannot set it) get, whenever the query has no hits in such a dimension, a null element in the returned list (a NullPointerException in callers that read each entry; with -ea Lucene's own 'assert facetResult != null' throws inside getTopDims), and when topNDims is small the hit-less dimension can take the slot of one that has hits (SSDV getTopDims(1, 10) = [null] instead of [b]); default codec, IndexWriterConfig and searcher, two documents suffice, every release from 9.2.0 (SSDV) or 9.3.0 (taxonomy) to 10.6.0 (10.6.0 by reading) and main; getAllDims, getTopChildren, hits, drill-down, single-valued dimensions and requireDimCount dimensions are correct, and nothing is written to the index.
Summary
You mark facet dimensions a and b as multi-valued (FacetsConfig#setMultiValued, requireDimCount left at its default false), index one document with a=a1 and one with b=b1, search for the second document only and call getTopDims(10, 10), expecting [b], the same as getAllDims(10), as the Facets#getTopDims javadoc says. Taxonomy and SortedSetDocValues facets instead return [null, b] (with -ea, getTopDims throws AssertionError at Lucene's own 'assert facetResult != null'), and SSDV getTopDims(1, 10) returns [null], dropping b, the only dimension with a hit. For such dimensions getTopDims uses the placeholder value -1 without checking for hits, the admission test 'value != 0' lets -1 through, and the per-dimension result of a dimension without hits is null.
Introduced
7c33f04d3746f855479da5177df8f19b6fdbbe84 (main, apache/lucene#747 'LUCENE-10325: Add getTopDims functionality to Facets', by Yuti-G, merged by gsmiller; issue #11361), first released through the branch_9x backport 77ccf2fb359ee926a3776ec4a78b2c0d95ee45fb (#773) in 9.2.0: it added Facets#getTopDims together with the SortedSetDocValuesFacetCounts override, whose 'dimCount != 0' admission already let the -1 placeholder of a hit-less multi-valued dimension through. Copies of the same logic: ConcurrentSortedSetDocValuesFacetCounts ef43242d77a (#777; branch_9x #889, 87655fd015c), 9.2.0; taxonomy facets got the same admission in #779 and #806 (main) and #889 (branch_9x), 9.2.0, and the null-element form of this bug from LUCENE-10585 (#915, 8db1e41fc07; branch_9x 075f5103e9a), 9.3.0, which made getTopChildren and getAllDims return null for such a dimension but left the getTopDims admission unchanged; #12966 (9ba4af7b883) moved the code into TaxonomyFacets and kept it. [read: git log -S and git blame over the full history; exec: single-file swaps on the 9.2.0 jars: SSDV without the #773 override passes, taxonomy and concurrent SSDV files from 87655fd015c^ pass, taxonomy files from 075f5103e9a reproduce the 9.3.0 behaviour] (2022-03-28 (#747 merged 22:54:07Z; backport #773 merged 2022-03-29T00:06:38Z); the concurrent SSDV and taxonomy overrides 2022-05-13 (#777, #779, #806, #889); the taxonomy null-element form 2022-05-29 (#915))
First affected version
9.2.0 [exec] (the first release with Facets#getTopDims): SortedSetDocValuesFacetCounts and ConcurrentSortedSetDocValuesFacetCounts return the null element there (no AssertionError with -ea yet; the assert came with LUCENE-10585 in 9.3.0); taxonomy facets from 9.3.0 [exec] (in 9.2.0 taxonomy getAllDims also listed the hit-less dimension with value -1 and no children, so getTopDims agreed with it); 9.1.0 has no getTopDims [exec: compile error, the only missing symbol for the repro is getTopDims(int,int)]
Affected versions
Every release from 9.2.0 to 10.5.2 (31 releases), 10.6.0 (by reading) and main ea57360912ed; not fixed upstream. [exec] follow-up: a 13-test JUnit (8 getTopDims cases over FastTaxonomyFacetCounts, TaxonomyFacetIntAssociations, TaxonomyFacetFloatAssociations, SortedSetDocValuesFacetCounts and ConcurrentSortedSetDocValuesFacetCounts, 3 getAllDims checks, 2 controls) fails 8 of 13 on every release from 9.3.0 to 10.5.2 and on main, with -ea (AssertionError inside Lucene) and with -da (null element); 9.2.0 fails 6 of 13 (SSDV and concurrent SSDV null elements; taxonomy getAllDims lists the hit-less dimension); the controls pass everywhere; the candidate fix makes 13 of 13 pass on main. The independent reproduction's JUnit fails 5 of 6 on main, 10.5.2 and 9.12.3 (-ea and -da) and on 9.3.0 and 9.2.0 (-da); the finder's JUnit fails on main (-ea, -da), 10.5.2, 9.12.3, 9.4.2 and 9.2.0 (-da). [read] branch_10x and branch_10_6 have TaxonomyFacets.java and AbstractSortedSetDocValueFacetCounts.java byte-identical to 10.5.2 (not executed); upstream main cf398bfb142c (2026-10-26) still has the -1 assignment, the '!= 0' admission and the assert. All runs on aarch64 with JDK 25
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
semi-formal: exhaustive check
Upstream and prior reports
none found (2026-10-26, read-only, the prior-report record, verdict new: 108 logged queries: a scripted prior-report search in three runs (30 GitHub issue/PR, 3 commit, 2 downstream over Elasticsearch, OpenSearch and Solr, 40 mailing-list over dev@, java-user@, issues@ and commits@, 8 JIRA, 7 web), all 411 open apache/lucene PRs filtered by changed file path, the full threads of #13287, #13191 and #13198, the review threads of #747, #777, #779, #806, #889, #915 and #12966, LUCENE-9952, and git log -S over the full history); no hit is this bug. Closest item: open, stale PR #13287 (stefanvodita, 2024-04-10, 'Don't assume non-zero aggregations in getTopDims or test checkResults', for #13191) deletes the same TaxonomyFacets admission line for a different defect (dims with hits and a zero aggregate dropped); it does not fix this bug and would widen it (the independent reproduction shadow run). Related, not reports: #11361 / #747 / #773 (the feature; its review agreed on -1 for multi-valued dims without dim counts and on skipping dims whose value is 0, and nobody noted that a -1 dim can have no hits), #11524 / #777 / #779 / #806 / #889 (the optimisation), #915 and #12966 (refactors), #11565 (a test NPE that confirmed the null-for-no-hits convention of getTopChildren), #12585 (non-positive aggregation values), #12862 (same files, getSpecificValue only). Not fixed upstream (main cf398bfb142c, 2026-10-26, and branch_10x still contain the -1 assignment, the '!= 0' admission and the assert).
Lucene commit tested
main a9779ef3c193 (commit of 2026-10-26; tested 2026-10-27) through gradle in a scratch worktree and through the standalone JUnit runner, with assertions on and off; the 10.5.2, 9.12.3, 9.3.0 and 9.2.0 release jars (this doc's test, unchanged). Earlier checks ran on main ea57360912ed, whose facet module is identical, and the follow-up's history test on every release from 9.1.0 to 10.5.2. The recommended fix was validated in the scratch worktree: the new test, the three closest facet suites, the whole lucene/facet suite (40 suites, 314 tests) with assertions on and off, and :lucene:facet:check -x test.

Lucene and upstream sources

Finding record
  • lucene/facet/src/java/org/apache/lucene/facet/taxonomy/TaxonomyFacets.java:300-310 (createFacetResult returns null when childCount == 0), :566-577 (getAllDims drops null results), :619-668 (getTopDims loop over root children: multi-valued dims take the indexed dim count or the -1 placeholder at :629-638, admission 'valueComparator.compare(dimValue, 0) != 0' at :652), :671-689 (result phase; createFacetResult at :685, 'assert facetResult != null' at :686, the null stored at :687)
  • lucene/facet/src/java/org/apache/lucene/facet/sortedset/AbstractSortedSetDocValueFacetCounts.java:118-126 (getAllDims drops null results), :174-183 (dimCount = -1 for multi-valued dims without requireDimCount), :200 (admission 'dimCount != 0'), :228-232 (comment 'should not be null since only dims with non-zero values were considered earlier', the assert, the null stored)
  • lucene/facet/src/java/org/apache/lucene/facet/Facets.java:52-67 (getAllDims 'any dimension that had hits'; getTopDims 'Results should be the same as calling getAllDims and then only using the first topNDims'; requireDimCount advice 'to ensure accurate counts are available')
  • lucene/facet/src/java/org/apache/lucene/facet/FacetsConfig.java:120-124 (requireDimCount 'unusual (default is false)'), :142-151 (getDefaultDimConfig javadoc 'multivalue = true' for dimensions not known beforehand), :305-307 ('AssociationFacetField cannot requireDimCount')
The finding in 30 seconds
  • lucene/facet/src/java/org/apache/lucene/facet/Facets.java:52-72
  • lucene/facet/src/java/org/apache/lucene/facet/taxonomy/TaxonomyFacets.java:301-310, :572-575, :594-690 (placeholder :629-638, admission :652, result phase :671-689)
  • lucene/facet/src/java/org/apache/lucene/facet/sortedset/AbstractSortedSetDocValueFacetCounts.java:121-125, :142-235 (placeholder :174-183, admission :200, result phase :215-234), :362-371
  • lucene/facet/src/java/org/apache/lucene/facet/FacetsConfig.java:120-124, :305-307
Who is affected
  • lucene/facet/src/java/org/apache/lucene/facet/FacetsConfig.java:120-124 (requireDimCount 'unusual (default is false)'), :142-151 (getDefaultDimConfig, 'multivalue = true'), :305-307 (AssociationFacetField cannot requireDimCount)
  • lucene/facet/src/java/org/apache/lucene/facet/Facets.java:52-56 (getAllDims, 'sparse' faceting)
  • lucene/facet/src/java/org/apache/lucene/facet/taxonomy/TaxonomyFacets.java:656-663 (queue replacement updates dim and value, not dimOrd)
  • lucene/facet/src/java/org/apache/lucene/facet/sortedset/AbstractSortedSetDocValueFacetCounts.java:153-157, :200-209 and TaxonomyFacets.java:610-614, :652-663 (queue order: value, then name; a -1 dimension is outranked by any value above -1)
Why it looks unintended
  • lucene/facet/src/java/org/apache/lucene/facet/Facets.java:25, :52-64
  • lucene/facet/src/java/org/apache/lucene/facet/taxonomy/TaxonomyFacets.java:635-637, :686; lucene/facet/src/java/org/apache/lucene/facet/sortedset/AbstractSortedSetDocValueFacetCounts.java:229-230
  • lucene/facet/src/java/org/apache/lucene/facet/FacetsConfig.java:120-124, :305-307
  • lucene/facet/src/test/org/apache/lucene/facet/sortedset/TestSortedSetDocValuesFacets.java:63-130, :176, :203-205, :299-306
  • lucene/facet/src/test/org/apache/lucene/facet/taxonomy/TestTaxonomyFacetAssociations.java:84-91, :211-240
Suggested fix
  • lucene/facet/src/java/org/apache/lucene/facet/taxonomy/TaxonomyFacets.java:467-516 (getTopChildrenForPath: childCount counts children with count > 0, dense and sparse)
  • lucene/CHANGES.txt:4258 (LUCENE-10488: Facets#getTopDims optimized for taxonomy faceting)

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

Repro files

About the diagrams

A light summary card, made in parallel with this doc from an executed per-call trace on main a9779ef3c193 (assertions on and off, and with the finder's fix and the recommended fix shadowed in as controls; both print the same); the PNG is the summary card.

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.