All findings · Lucene finding · LFV-080 · memoryindex-index-dimensions

MemoryIndex reports the total number of point dimensions as the number of index dimensions

A point field that stores 2 values but indexes only the first (FieldType.setDimensions(2, 1, 4)) answers point queries in a normal index, but in a MemoryIndex every such query throws IllegalArgumentException: MemoryIndex reports 2 index dimensions instead of 1. A one-line copy error, in every release since 7.6.0.

Summary

Priority
Low, rank 1137MemoryIndex reports total point dimensions as index dimensions, so point queries on custom selectively indexed fields throw IllegalArgumentException, loud with a trivial fix and a tiny population.
Impact
crash
Since
7.6.0 (LUCENE-8496: commit 1118299c338 on master, backport 804afbfd47c on branch_7x, 2018)introduced 2018-10-08 · about 8 years
Affected
7.6.0-10.6.0 and main
Status
documented
Found by
non-formal: code reading, then executed

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

The finding in 30 seconds

A point field can store more values than it indexes; MemoryIndex mixes up the two counts.

Background: selectively indexed points

  1. A point field stores N numbers per value, its dimensions. FieldType.setDimensions(N, M, bytes) builds the index on the first M only and keeps the rest alongside (selective indexing, since 7.6.0). Every built-in point type except ShapeField indexes all N.
  2. PointValues reports both counts: getNumDimensions() (stored) and getNumIndexDimensions() (indexed). A point query must have exactly as many dimensions as the field indexes, or it throws IllegalArgumentException.
  3. MemoryIndex is a one-document, in-memory index for testing a document against many queries, for example inside Monitor.
Diagram of the bug. A field type with 2 dimensions of which 1 is indexed, setDimensions(2, 1, 4), and one point (10, 5) in a MemoryIndex. MemoryIndex's FieldInfo stores 1 index dimension, but its PointValues reports getNumIndexDimensions() = 2, the same as getNumDimensions(). IntPoint.newExactQuery("f", 10) has 1 dimension, so PointRangeQuery rejects it: IllegalArgumentException, field "f" was indexed with numIndexDimensions=2 but this query has numDims=1. The same document in an IndexWriter index reports 1 index dimension and the query matches once. The fix returns getPointIndexDimensionCount().
One point (10, 5) of a field with 2 dimensions, 1 indexed: MemoryIndex's FieldInfo says 1 index dimension, its PointValues says 2, so the 1-dimension query is rejected; an IndexWriter index says 1 and matches. Select the still to open it at full size. Generated with AI, checked against the code.

The finding, step by step

  1. Define a point type that stores two ints per value but indexes only the first: type.setDimensions(2, 1, Integer.BYTES). Add the point (10, 5) to a MemoryIndex.
  2. Search it with IntPoint.newExactQuery("f", 10), a 1-dimension query on the indexed value. You expect a match: an IndexWriter index with the same document returns count 1.
  3. Instead search throws IllegalArgumentException: field="f" was indexed with numIndexDimensions=2 but this query has numDims=1, with assertions on and off.
  4. Why: MemoryIndex's PointValues.getNumIndexDimensions() returns the total count, 2. Its body is a copy of getNumDimensions() right above it; the field's own FieldInfo correctly says 1. Point queries must match the index count, so they reject the legal query.
  5. Every exact, range and set query on the indexed dimension fails, in every release from 7.6.0 (2018) to 10.6.0 and on main. Through Monitor (a one-document batch) the stored query is reported as an error and the document does not match. Built-in point fields and shape queries are not affected.

Who is affected

Only applications that define their own selectively indexed point type (setDimensions(n, m < n, bytes)) and query it in a MemoryIndex: directly, through MemoryIndex.fromDocument, or through Monitor (one-document batches). Then every exact, range and set query on its indexed dimensions fails, on any data. Built-in point types other than ShapeField index all dimensions, and shape queries never check the count.

Mechanism, terms and numbers488 words

FieldType.setDimensions(dimensionCount, indexDimensionCount, bytes) lets a point field store more dimensions than it indexes (selective indexing, LUCENE-8496, 7.6.0). PointValues reports the two counts separately, and PointRangeQuery, PointInSetQuery and the sandbox MultiRangeQuery require a query to have exactly getNumIndexDimensions() dimensions. MemoryIndex stores both counts correctly in its FieldInfo, but its PointValues returns the total count from getNumIndexDimensions() too (MemoryIndex.java:2194-2197, a copy of getNumDimensions() at :2189-2192). So a MemoryIndex holding the point (10, 5) of a 2-dimension, 1-index-dimension field rejects IntPoint.newExactQuery("f", 10) with IllegalArgumentException, while an IndexWriter index of the same document answers it. Monitor, which matches a single document through a MemoryIndex, reports the stored query as an error and no match. Every release from 7.6.0 to 10.6.0 and main is affected; built-in types are not, because they index all their dimensions (or, for shapes, are queried by shape queries that never read the count). The fix is one line, but on its own it turns some set queries, on documents that hold several values in the field, from an exception into a silent miss because of the separate PointInSetQuery bug LFV-006, so LFV-006's query-side fix should land first or together with it.

Terms

  • Data dimensions (getNumDimensions(), FieldInfo.getPointDimensionCount()): how many numbers each point value stores.
  • Index dimensions (getNumIndexDimensions(), FieldInfo.getPointIndexDimensionCount()): how many of them, from the first, the index is built on, and how many dimensions a point query on the field must have.
  • Selectively indexed field: a field with fewer index dimensions than data dimensions, made with FieldType.setDimensions(n, m < n, bytes) (FieldType.java:309-310, "Enables points indexing with selectable dimension indexing"). The two-argument setDimensions(n, bytes) that the other built-in point types use passes n for both (:305-306). The only built-in selectively indexed type is ShapeField (7 dimensions, 4 indexed, ShapeField.java:52).

The bug

  1. MemoryIndex stores both counts in the field's FieldInfo (createFieldInfo, MemoryIndex.java:775-776; setPointDimensions, :745-750; the FieldInfo rebuilt for doc values keeps both, :904-905).
  2. Its PointValues returns the data count from both methods: getNumDimensions() at :2189-2192 and getNumIndexDimensions() at :2194-2197 both return info.fieldInfo.getPointDimensionCount();.
  3. PointRangeQuery.rewrite checks every leaf (PointRangeQuery.java:544-549) and throws unless getNumIndexDimensions() equals the query's dimension count (:621-633). PointInSetQuery (PointInSetQuery.java:168-174) and the sandbox MultiRangeQuery (MultiRangeQuery.java:299-306) have the same check. A query built for the field's real index count is rejected.
  4. Shape queries never read getNumIndexDimensions(), so ShapeField in a MemoryIndex is not affected by this check. (Its reported count is wrong too on stock code, 7 instead of 4, but a LatLonShape box query gives the same hit and miss with and without the fix; executed.)

The numbers

Field type setDimensions(2, 1, Integer.BYTES), one point (10, 5), query IntPoint.newExactQuery("f", 10). MemoryIndex: FieldInfo.getPointIndexDimensionCount()=1, PointValues.getNumDimensions()=2, PointValues.getNumIndexDimensions()=2, and the query throws. IndexWriter index of the same document: getNumDimensions()=2, getNumIndexDimensions()=1, count 1.

Exposure and likelihood290 words
  • Who is hit. Code that puts a custom selectively indexed point field into a MemoryIndex: MemoryIndex.addField, MemoryIndex.fromDocument (MemoryIndex.java:531-564), or Monitor, whose single-document batch is a MemoryIndex (DocumentBatch.java:101-112). Only one-document batches are hit: a batch of several documents is built with an IndexWriter over a ByteBuffersDirectory (DocumentBatch.java:55-62, :66-92), which reports the right count. Typical uses are matching one incoming document against many stored queries and testing a document against a query before indexing it.
  • Opt-in. The other built-in point types index all their dimensions; ShapeField is selectively indexed but its queries never read the count. Such a custom type is legal and works with IndexWriter.
  • What happens. Every PointRangeQuery (exact or range), PointInSetQuery and sandbox MultiRangeQuery with the field's real index dimension count throws IllegalArgumentException, deterministically, with assertions on and off. One point is enough.
  • Through Monitor it becomes a missing match. Monitor catches the exception and reports it per query (Monitor.java:294-302). Executed: a stored IntPoint.newExactQuery("f", 10) and a document holding (10, 5) give matchCount=0 errors={q1=java.lang.IllegalArgumentException: field="f" was indexed with numIndexDimensions=2 but this query has numDims=1}, with assertions on and off; with the fix matchCount=1. An application that does not read getErrors() sees no match.
  • The reverse (executed): a query with as many dimensions as the field stores passes the check on a MemoryIndex and is evaluated against the full stored value: IntPoint.newRangeQuery("f", new int[] {10, 5}, new int[] {10, 5}) matches and {10, 6} does not, while an IndexWriter index rejects both (numIndexDimensions=1 but this query has numDims=2). Not behaviour to rely on: with the fix, a MemoryIndex rejects them too.
  • How likely. Rare: no prior public report found since 7.6.0 (2018).

Minimal repro

One JUnit test, one method, public API only: one point (10, 5) of a 2-dimension, 1-index-dimension field in a MemoryIndex, queried with IntPoint.newExactQuery("f", 10). Deterministic; fails with assertions on and off. It catches the exception only to name it in the failure.

TestMemoryIndexSelectiveIndexing.java (JUnit, one test)38 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.index.memory;

import org.apache.lucene.document.Field;
import org.apache.lucene.document.FieldType;
import org.apache.lucene.document.IntPoint;
import org.apache.lucene.tests.util.LuceneTestCase;

public class TestMemoryIndexSelectiveIndexing extends LuceneTestCase {

  public void testExactQueryOnOneOfTwoDimensions() {
    FieldType type = new FieldType();
    type.setDimensions(2, 1, Integer.BYTES);
    MemoryIndex index = new MemoryIndex();
    index.addField(new Field("f", IntPoint.pack(10, 5), type), null);
    try {
      assertTrue(index.search(IntPoint.newExactQuery("f", 10)) > 0);
    } catch (IllegalArgumentException e) {
      // expected: a match on the 1 indexed dimension; actual: getNumIndexDimensions() returns 2
      throw new AssertionError("expected f:10 to match, but got " + e, e);
    }
  }
}
Command
cp TestMemoryIndexSelectiveIndexing.java $LUCENE/lucene/memory/src/test/org/apache/lucene/index/memory/
cd $LUCENE && ./gradlew :lucene:memory:test --tests TestMemoryIndexSelectiveIndexing -Ptests.seed=DEADBEEF -Ptests.asserts=true
# assertions off: -Ptests.asserts=false instead (same result)
Expected
IntPoint.newExactQuery("f", 10) matches the point (10, 5), as it does on an IndexWriter index (count 1).
Observed
IllegalArgumentException: field="f" was indexed with numIndexDimensions=2 but this query has numDims=1 from PointRangeQuery.rewrite, with assertions on and off.

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

More files, environment, full output271 words

The test sits in org.apache.lucene.index.memory next to the module's tests but uses no package-private API. It uses no randomness, and no seed sensitivity was observed.

As the regression test: the same test, for example as a new method of TestMemoryIndex; the try/catch can go (assertTrue(index.search(IntPoint.newExactQuery("f", 10)) > 0);). It fails on stock main and passes with the fix, with assertions on and off (gradle); :lucene:memory:check -x test passes with the fix and the test in place.

Not shown by this test, executed elsewhere: set queries (on stock code they throw an IllegalArgumentException too, with PointInSetQuery's own wording was indexed with numIndexDims=2 but this query has numIndexDims=1), Monitor (an error entry and no match), and the release jars 7.6.0 to 10.6.0.

Environment

lucenemain 645d4fe14a20 (11.0.0-SNAPSHOT); the same failure on every release from 7.6.0 to 10.6.0; 7.5.0 has no selective indexing
jdkOpenJDK 25.0.3
osLinux, aarch64
Command (environment)
cp TestMemoryIndexSelectiveIndexing.java $LUCENE/lucene/memory/src/test/org/apache/lucene/index/memory/
cd $LUCENE && ./gradlew :lucene:memory:test --tests TestMemoryIndexSelectiveIndexing -Ptests.seed=DEADBEEF -Ptests.asserts=true
# assertions off: -Ptests.asserts=false instead (same result)

Observed output

# gradle, main 645d4fe14a20, -Ptests.seed=DEADBEEF -Ptests.asserts=true (the same with =false); excerpt: the SeedInfo frame and the org.apache.lucene.core@11.0.0-SNAPSHOT/ module prefixes are left out
TestMemoryIndexSelectiveIndexing > testExactQueryOnOneOfTwoDimensions FAILED
    java.lang.AssertionError: expected f:10 to match, but got java.lang.IllegalArgumentException: field="f" was indexed with numIndexDimensions=2 but this query has numDims=1
        at org.apache.lucene.index.memory.TestMemoryIndexSelectiveIndexing.testExactQueryOnOneOfTwoDimensions(TestMemoryIndexSelectiveIndexing.java:35)

        Caused by:
        java.lang.IllegalArgumentException: field="f" was indexed with numIndexDimensions=2 but this query has numDims=1
            at org.apache.lucene.search.PointRangeQuery.checkValidPointValues(PointRangeQuery.java:632)
            at org.apache.lucene.search.PointRangeQuery.rewrite(PointRangeQuery.java:548)
            at org.apache.lucene.search.IndexSearcher.rewrite(IndexSearcher.java:873)
            at org.apache.lucene.search.IndexSearcher.rewrite(IndexSearcher.java:884)
            at org.apache.lucene.search.IndexSearcher.search(IndexSearcher.java:753)
            at org.apache.lucene.index.memory.MemoryIndex.search(MemoryIndex.java:1115)
            at org.apache.lucene.index.memory.TestMemoryIndexSelectiveIndexing.testExactQueryOnOneOfTwoDimensions(TestMemoryIndexSelectiveIndexing.java:32)

# releases 7.6.0 to 10.6.0: the same message; the check sits in the weight's scorerSupplier up to 10.2.2 and in rewrite from 10.3.0

Why it looks unintended

  • PointValues documents getNumIndexDimensions() as "how many dimensions are used for the index"; the field declared 1, and MemoryIndex's own FieldInfo holds 1.
  • An IndexWriter index of the same document reports 1 index dimension, and the query matches.
  • The body is a copy of getNumDimensions() from the commit that added selective indexing; no MemoryIndex test uses such a field.
The full argument175 words
  • Contract. PointValues.java:483-487: getNumDimensions() "Returns how many dimensions are represented in the values", getNumIndexDimensions() "Returns how many dimensions are used for the index". FieldType.setDimensions(int, int, int): "Enables points indexing with selectable dimension indexing" (FieldType.java:309-310); (2, 1, 4) passes its validation (:323-329).
  • MemoryIndex promises arbitrary queries. createSearcher(): "a searcher that can be used to execute arbitrary Lucene queries" (MemoryIndex.java:1074); search(Query): "an arbitrary Lucene query to run against this index" (:1104). No javadoc, CHANGES or MIGRATE entry excludes selectively indexed points.
  • Parity. IndexWriter reports 1 index dimension and count 1 for the same input; the independent reviewer's 3-data / 2-index LongPoint control on IndexWriter matches too.
  • History. LUCENE-8496 (1118299c338, 2018-10-08) split MemoryIndex's getNumDimensions() into getNumDataDimensions() and a new getNumIndexDimensions() with the same body, in the commit that also passed pointIndexDimensionCount() into MemoryIndex's FieldInfo and made the point queries check getNumIndexDimensions(). Two later edits (LUCENE-9149 rename, 2020; #16387, 2026) kept the value.
  • No test. lucene/memory/src/test only calls setDimensions(1, 4) (TestMemoryIndex.java:673, :699).
The devil's-advocate check95 words

The independent reviewer's strongest case: PointValues, PointRangeQuery and PointInSetQuery are @lucene.experimental, MemoryIndex has a simplified single-document point tree, and its tests never use unequal counts, so selective indexing might have been meant only for BKD-backed readers.

Why it fails: no such limitation is documented; MemoryIndex promises arbitrary queries; the introducing commit deliberately stored the index count in MemoryIndex's FieldInfo; IndexWriter handles the same input; and the two methods have different documented meanings, so returning the same value from both cannot be the design. The reviewer rated the argument weak. Verdict: a defect.

Suggested fix

Recommended A. Return the index dimension count, landed with LFV-006's PointInSetQuery fix

Return info.fieldInfo.getPointIndexDimensionCount() from getNumIndexDimensions() (one line in MemoryIndex.java), and land it after, or together with, the query-side fix of the separate PointInSetQuery bug LFV-006: with the one line alone, some set queries on documents that hold several values in the field stop throwing and silently miss instead.

       @Override
       public int getNumIndexDimensions() {
-        return info.fieldInfo.getPointDimensionCount();
+        return info.fieldInfo.getPointIndexDimensionCount();
       }
All options and trade-offs3 options ยท 660 words

Both A and A with LFV-006's query-side fix were run through gradle in a scratch worktree of 645d4fe14a20. The MemoryIndex line is the whole change for this issue; the coupling is the order of landing.

A. Return the index dimension count, landed with LFV-006's PointInSetQuery fix (validated by the checks) (recommended)

One line in MemoryIndexPointValues.getNumIndexDimensions() (MemoryIndex.java:2196). Land it after, or together with, LFV-006's query-side fix in PointInSetQuery (merge visitor only when numDims == 1 && values.getNumDimensions() == 1; SinglePointVisitor compares only the index-dimension prefix).

       @Override
       public int getNumIndexDimensions() {
-        return info.fieldInfo.getPointDimensionCount();
+        return info.fieldInfo.getPointIndexDimensionCount();
       }

Trade-offs

  • The MemoryIndex change is safe for every built-in type: the other built-in point types index all their dimensions, so their index count equals their data count; ShapeField is selectively indexed (7 dimensions, 4 indexed), but shape queries never read the count: a LatLonShape box query in a MemoryIndex gives the same results with and without the fix (executed).
  • Why the pairing: with the one line alone, some set queries, on documents that hold several values in the field, go from an exception to a silent miss; single-valued documents, and values added in increasing order of the indexed dimension, still match (executed). 2 dimensions / 1 indexed, values (30, 5) then (10, 5), IntPoint.newSetQuery("f", 10): no match, with assertions on and off. MemoryIndex sorts values only when the field has 1 dimension in total (MemoryIndex.java:1332-1338), and PointInSetQuery's 1-dimension merge visitor needs increasing indexed values (PointInSetQuery.java:203, :282). 3 dimensions / 2 indexed, values (1, 2, 100) and (3, 4, 300), a 2-dimension set query for (1, 2): no match with assertions off, and Lucene's own assert packedValue.length == pointBytes.length (PointInSetQuery.java:414) with assertions on. Both are LFV-006's root cause, reached through MemoryIndex.
  • The tested exact and range queries are correct with the one line alone: the repro, 1-dimension exact and range queries on the 2/1 field (through addField and fromDocument), a sandbox MultiRangeQuery, and the independent reviewer's 3/2 range query. PointRangeQuery compares only the first numDims dimensions of a value or cell (PointRangeQuery.java:134-147, :594-618).
  • Behaviour change: with the fix, a query with as many dimensions as the field stores (2 on the 2/1 field) throws on a MemoryIndex, as it already does on an IndexWriter index; stock code matched it against the full stored value (executed).

What the checks validated

  • Gradle in a scratch worktree of 645d4fe14a20 (2026-11-01): the repro fails on stock main and passes with the one line, each with -Ptests.asserts=true and =false.
  • With the one line: :lucene:memory:test 4 classes with the repro, 52 tests, 0 failures; :lucene:monitor:test 32 classes, 181 tests, 0 failures, 1 skipped; :lucene:memory:check -x test (google-java-format, ecj lint, forbidden APIs) passes with the test in place.
  • With the one line plus LFV-006's query-side fix: the repro and both set-query checks pass with assertions on and off; :lucene:memory:test 5 classes, 54 tests, 0 failures; core TestPointQueries 56 tests, 0 failures, 2 skipped.
  • Earlier, the same line: the repro with the fix shadowed in (assertions on and off); the independent reviewer's 4-test class 4/0 and upstream TestMemoryIndex 30/0; the Monitor check matchCount=1.

B. The one line alone

The same diff, landed before LFV-006 is fixed.

Trade-offs

Fixes exact and range queries, but turns some set queries, on documents that hold several values in the field, from a loud exception into a silent miss (executed above). Not recommended unless LFV-006's fix lands in the same release.

C. The one line plus a MemoryIndex-local sort (not tested)

Also sort point values when the field has one index dimension (MemoryIndex.java:1334 tests the total count today), keeping the per-dimension min/max loop.

Trade-offs

Would make 1-index-dimension set queries in a MemoryIndex correct without the core change, but not multi-index-dimension set queries (SinglePointVisitor), and nothing for on-disk indexes, so LFV-006 is still needed. Only worth it if LFV-006's fix is delayed. Not built.

Downstream note

Downstream services that match documents against stored queries in a MemoryIndex hit it only with a custom selectively indexed point type, as an exception or, through Monitor, as a per-query error and a missing match.

How it was found

Code reading, not a formal method. While verifying the PointInSetQuery selective-indexing bug (LFV-006), a verifier noticed that MemoryIndex's getNumIndexDimensions() has the body of getNumDimensions(). A follow-up executed it with its own tests and a fix matrix; a one-test repro, an independent reviewer from another model family (OpenAI GPT-5.6) and a sweep over every release from 7.5.0 to 10.6.0 confirmed it.

The method in full131 words

Method: non-formal (a side observation from code reading). In the 2026-10-22 study, the verifier of the selective-indexing PointInSetQuery bug (LFV-006) read MemoryIndex because it sorts points only for 1-dimensional fields, and noted that getNumIndexDimensions() returns getPointDimensionCount(). A follow-up executed it with its own tests (testS2MemoryIndexExact, testS2MemoryIndexSet) and a fix matrix that showed that with the fix alone its set query, on a document holding (30, 5) then (10, 5), becomes a silent miss.

The hunt then cut it to the one-test repro, re-ran it on main 645d4fe14a20 and on 10.5.2, 10.6.0 and 9.12.3 with controls, had an independent reviewer from another model family (OpenAI GPT-5.6) try to refute it with its own 3-data / 2-index-dimension test (verdict real, high confidence), and ran the history sweep over 7.5.0-10.6.0.

Prior reports, history and checks

Prior reports

No prior report found in GitHub issues, PRs and commits (all 409 open PRs' changed files included), the JIRA archive, the Lucene mailing lists, downstream projects or the web (103 queries, 2026-11-01). Not fixed upstream as of 2026-11-01.

Searches4 searches

Closest items, none a report of this bug: #9542 (LUCENE-8496, the introducing change; its comments and the review of apache/lucene-solr#451 never mention MemoryIndex); #8920 (LUCENE-7869 "MemoryIndex should sort 1d points", the 1-D-only sort behind the silent-miss caveat); #8149 (LUCENE-7093, points in MemoryIndex, before selective indexing); #10189 (LUCENE-9149, renamed the getter in the buggy line); #16387 (the last edit of the method, value unchanged); #9567 (LUCENE-8521, shape encoding on selective indexing); #8730 (LUCENE-7679, a different MemoryIndex FieldType bug). Eight open PRs touch lucene/memory/src/java; none changes a dimension line. The related LFV-006 is another finding of this hunt, not an upstream report. Limits: GitHub search returns at most 50 results per query; mailing-list search returns snippets only.

What was searched, when, and what turned up
WhereWhenHowFound
apache/lucene issues, PRs and commits (incl. the migrated JIRA issues)2026-11-01a scripted prior-report search (40 GitHub and 3 commit queries) plus manual GitHub searches, e.g. MemoryIndex getNumIndexDimensions, MemoryIndex numIndexDimensions, MemoryIndex selectively indexed, MemoryIndex setDimensions, MemoryIndex PointRangeQuery, "was indexed with numIndexDimensions", getNumIndexDimensionsno report; only unrelated items
Open PRs2026-11-01changed files of all 409 open apache/lucene PRs8 touch lucene/memory/src/java; none changes a dimension line in MemoryIndex.java
JIRA archive, mailing lists, downstream, web2026-11-014 JQL queries; 20 queries over dev@, java-user@, issues@ and commits@; 8 downstream queries; 6 web queriesonly notifications, unrelated threads and how-to pages
Earlier searches2026-10-22, 2026-11-01the study's follow-up and an earlier search (8 GitHub queries)no report

History

Introduced in 7.6.0 by LUCENE-8496 (1118299c338, 2018-10-08; branch_7x backport 804afbfd47c), the change that added selective indexing; the line kept its value through two later edits. Every release from 7.6.0 to 10.6.0 and main fails; 7.5.0 cannot express the trigger.

Versions tested5 rows

Introduced by 1118299c338 "LUCENE-8496: Selective indexing - modify BKDReader/BKDWriter to allow users to select a fewer number of dimensions to be used for creating the index ..." (Nicholas Knize, 2018-10-08; issue LUCENE-8496, now #9542; PR apache/lucene-solr#451, landed as direct commits). On branch_7x: 804afbfd47c (2018-10-18) with the same MemoryIndex change; an earlier backport 5b7739a081a (committed 2018-10-10) was reverted the same day by ac11c9e5b17, before any release. CHANGES.txt lists LUCENE-8496 under 7.6.0 New Features.

Lineage of the line: c1dfeb8ef85 (LUCENE-7093, 2016, correct then) -> 1118299c338 (bug) -> 206a70e7b79 (LUCENE-9149, 2020, rename, same value) -> 0c57b5ae240 (#16387, 2026, drops throws IOException, same value).

Versions and branches executed, with results
Version or commitVerdictResult
main 645d4fe14a20 affectedminimal repro fails with assertions on and off (gradle and standalone); passes with the fix
10.6.0, 10.5.2, 9.12.3 affectedminimal repro fails (release jars; 10.5.2 with assertions on and off)
7.6.0 to 10.6.0 (66 releases) first affected 7.6.0history probe fails on every release; the fully indexed control passes
7.5.0 no APIthe trigger cannot be written: no setDimensions(int, int, int) (compile error)
upstream main f815e679bf6c, branch_11x, branch_10_6 readthe same line

Independent checks

Confirmed with high confidence: a re-verification, an independent reviewer from another model family (OpenAI GPT-5.6) with its own test, and an 80-run history sweep all reproduced it; everyone agrees on the root cause and the one-line fix.

Checks and verdicts313 words

The impact is minor and agreed: a loud exception on a legal query (a missing match through Monitor), only for custom selectively indexed point types.

Each independent check and its verdict
CheckVerdictWhat it found
study follow-up real, low (exception)Own tests and a fix matrix: the exact and set queries throw on stock code; the fix alone makes its set query (a document holding (30, 5) then (10, 5)) a silent miss; with LFV-006's query-side fix both match.
re-verification realMinimal repro fails on main with assertions on and off and on 10.5.2, 10.6.0 and 9.12.3; FieldInfo says 1 while PointValues says 2; the IndexWriter control matches; the fix passes.
cross-model review, refute lens (OpenAI GPT-5.6) real, highOwn 3-data / 2-index LongPoint test fails 2 of 4 with assertions on and off (wrong metadata, then the exception); 2/2 MemoryIndex and 3/2 IndexWriter controls pass; the fix passes 4/4 and upstream TestMemoryIndex 30/0. Intended-behaviour argument: weak.
history sweep 7.6.0 to 10.6.0 and main80 runs: every release from 7.6.0 fails; 7.5.0 cannot express the trigger; the fully indexed control passes everywhere it ran.
brief validation fix validatedGradle on a scratch worktree: the repro fails on stock main and passes with the fix; memory and monitor modules pass; the set-query caveat reproduced and resolved with LFV-006's fix; Monitor reports matchCount=0 and the exception on stock code, matchCount=1 with the fix.
fact checks of this doc (independent; cross-model, OpenAI GPT-5.6) bug confirmed; wording fixedRe-ran the repro on main and 10.6.0 (assertions on and off, standalone and gradle) and probed the set-query scope, the reverse case, the sandbox MultiRangeQuery and ShapeField. Both confirm the bug, the mechanism and the fix; the wording corrections they asked for (set-query scope, a stack-trace line, the shown diff, ShapeField, the scope of the history sweep) are applied.

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 notes811 words

Finding record

LFV-080, snapshot of 2026-11-06

Registry title
MemoryIndex reports the total number of point dimensions as the number of index dimensions
Area
memory / MemoryIndex
Priority
Low, rank 1137: MemoryIndex reports total point dimensions as index dimensions, so point queries on custom selectively indexed fields throw IllegalArgumentException, loud with a trivial fix and a tiny population.
Impact class
crash
Impact note
Only applications that define their own selectively indexed point type (FieldType.setDimensions(n, m < n, bytes)) and query it in a MemoryIndex, directly, through MemoryIndex.fromDocument or through Monitor's one-document batches: every exact, range and set point query on its indexed dimensions throws IllegalArgumentException (through Monitor a per-query error and no match), with -ea and -da and on any data; built-in point types are not affected (ShapeField's shape queries never read the count); every release from 7.6.0 to 10.6.0 and main [exec].
Summary
You add a selectively indexed point field (for example 2 dimensions, 1 index dimension) to a MemoryIndex and run a point query on it. You expect the query to work as on a normal index. Instead getNumIndexDimensions() returns the total dimension count and the query throws IllegalArgumentException.
Introduced
1118299c338 (LUCENE-8496 'Selective indexing - modify BKDReader/BKDWriter to allow users to select a fewer number of dimensions to be used for creating the index', Nicholas Knize, 2018-10-08; issue now apache/lucene#9542, PR apache/lucene-solr#451 landed as direct commits; branch_7x backport 804afbfd47c, 2018-10-18, first released in 7.6.0): it split MemoryIndex's getNumDimensions() into getNumDataDimensions() and a new getNumIndexDimensions() with the same body, in the commit that made the point queries check getNumIndexDimensions() [read: the line's git history, c1dfeb8ef85 (correct) -> 1118299c338 (bug) -> 206a70e7b79 and 0c57b5ae240 (same value); exec: 7.5.0 cannot express the trigger, 7.6.0 fails] (2018-10-08 (1118299c338 on master; branch_7x backport 804afbfd47c 2018-10-18))
First affected version
7.6.0 [exec] (7.5.0 has no setDimensions(int, int, int), so the trigger cannot be written [exec: compile error])
Affected versions
every release from 7.6.0 to 10.6.0 (66 releases) and main [exec: a main() port of the minimal repro fails on each, the fully indexed control passing; the JUnit repro fails on main 645d4fe14a20 (-ea and -da) and on 9.1.0, 9.12.3, 10.5.2 and 10.6.0]; not fixed upstream: upstream main f815e679bf6c (2026-11-01), branch_11x and branch_10_6 have the same line [read]; unaffected: 7.5.0 and earlier (no selective indexing, the trigger cannot be written) [exec on 7.5.0]
Status
documented (brief, checks and a per-finding doc exist)
Verification
Confirmed by AI-run checks: a cross-model review that agreed and a prior-report search; not yet triaged by a person.
Found by
non-formal: code reading
Upstream and prior reports
none found (2026-11-01, read-only: 103 queries, among them a scripted prior-report search over GitHub issues and PRs, commits, downstream repositories, the dev@, java-user@, issues@ and commits@ lists, the JIRA archive and the web, manual GitHub searches and the changed files of all 409 open apache/lucene PRs; earlier read-only GitHub searches 2026-10-22); closest items, none a report of this bug: #9542 (LUCENE-8496, the introducing change), #8920 (LUCENE-7869, MemoryIndex sorts 1-D points), #10189 (LUCENE-9149, renamed the getter in the buggy line), #16387 (the last edit of the method, value unchanged); not fixed upstream
Lucene commit tested
main 645d4fe14a20: the minimal repro through gradle with -Ptests.asserts=true and =false (stock and with the fix), :lucene:memory:test, :lucene:monitor:test and :lucene:memory:check -x test with the fix, the set-query checks with the fix alone and with LFV-006's query-side fix, and core TestPointQueries; a Monitor check run standalone. Earlier steps: main 645d4fe14a20, ea57360912ed (independent reviewer) and a9779ef3c193 (refresh), a main() port of the repro (same input and assertion) on the release jars 7.5.0 (cannot express the trigger) and every release from 7.6.0 to 10.6.0 (66 releases, all fail), and the JUnit test itself on 9.1.0, 9.12.3, 10.5.2 and 10.6.0. Fact checks re-ran the repro on main and 10.6.0 and probed the set-query scope, the reverse case, the sandbox MultiRangeQuery and ShapeField. MemoryIndex.java is unchanged on upstream main f815e679bf6c (2026-11-01, read).

Lucene and upstream sources

Finding record
  • a Lucene clone tag releases/lucene/10.5.2: lucene/memory/src/java/org/apache/lucene/index/memory/MemoryIndex.java:2121-2122
The finding in 30 seconds
  • lucene/memory/src/java/org/apache/lucene/index/memory/MemoryIndex.java:745-750, :775-776, :904-905, :2189-2197 (at 645d4fe14a20)
  • lucene/core/src/java/org/apache/lucene/document/FieldType.java:305-310; ShapeField.java:52 (at 645d4fe14a20)
  • lucene/core/src/java/org/apache/lucene/search/PointRangeQuery.java:544-549, :621-633; PointInSetQuery.java:168-174; lucene/sandbox/src/java/org/apache/lucene/sandbox/search/MultiRangeQuery.java:299-306 (at 645d4fe14a20)
Who is affected
  • lucene/memory/src/java/org/apache/lucene/index/memory/MemoryIndex.java:531-564; lucene/monitor/src/java/org/apache/lucene/monitor/DocumentBatch.java:101-112; Monitor.java:294-302 (at 645d4fe14a20)
  • lucene/monitor/src/java/org/apache/lucene/monitor/DocumentBatch.java:55-62, :66-92 (multi-document batches use IndexWriter; at 645d4fe14a20)
Why it looks unintended
  • lucene/core/src/java/org/apache/lucene/index/PointValues.java:102, :483-487; FieldType.java:309-329; PointRangeQuery.java:52; PointInSetQuery.java:51 (at 645d4fe14a20)
  • lucene/memory/src/java/org/apache/lucene/index/memory/MemoryIndex.java:1074, :1104; lucene/memory/src/test/org/apache/lucene/index/memory/TestMemoryIndex.java:673, :699 (at 645d4fe14a20)
Suggested fix
  • lucene/memory/src/java/org/apache/lucene/index/memory/MemoryIndex.java:1332-1338, :2194-2197; lucene/core/src/java/org/apache/lucene/search/PointInSetQuery.java:203, :282-340, :413-428 (at 645d4fe14a20)
  • lucene/core/src/java/org/apache/lucene/search/PointRangeQuery.java:134-147, :594-618 (at 645d4fe14a20)

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

Repro files

About the diagrams

Made in parallel with this doc from the code at 645d4fe14a20 and the repro's input and output.

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.