An n-gram tokenizer copies each gram into the term buffer, which it grows once, in its constructor, to 2 * maxGram chars. In int, 2 * 2,147,483,647 is -2, so the buffer is never grown and keeps its default 16 chars; the read buffer, 2 * maxGram + 1024 chars, gets 1022.
EdgeNGramTokenizer(minGram, maxGram)emits the prefixes of the text fromminGramtomaxGramcode points:(1, 17)turnsabcintoa,ab,abc.NGramTokenizeralso emits the grams that start later (b,bc,c). Typical use: prefix or substring autocomplete.- Each token is copied into the term attribute's char buffer, which starts at 16 chars (on a 64-bit JVM). These tokenizers grow it once, in the constructor, to
2 * maxGramchars (a code point can take 2 chars), and then copy every gram straight into it.
EdgeNGramTokenizer(1, Integer.MAX_VALUE) on a 17-char text: the constructor accepts the value, 2 * maxGram wraps to -2, so the read buffer gets 1022 chars and the term buffer stays at 16; the 17th prefix does not fit and incrementToken throws. The fix rejects the value when the tokenizer is created. Select the still to open it at full size. Generated with AI, checked against the code.- You build
new EdgeNGramTokenizer(1, Integer.MAX_VALUE)to mean "all prefixes, no upper limit" and tokenize the 17-char textabcdefghijklmnopq. The constructor checksmaxGramand accepts it. - You expect the 17 prefixes
a,ab, ...,abcdefghijklmnopq: whatmaxGram= 17 gives, and whatEdgeNGramTokenFiltergives for the sameInteger.MAX_VALUE. Or else anIllegalArgumentExceptionfrom the constructor. - You get 16 prefixes, then
ArrayIndexOutOfBoundsException: Index 16 out of bounds for length 16. Texts of up to 16 chars work; every longer text throws. - Why: the constructor sizes its buffers in
int.2 * 2,147,483,647wraps to -2, sotermAtt.resizeBuffer(-2)does nothing and the term buffer keeps its default 16 chars: the 17-char prefix does not fit. - So
addDocumentthrows for each document with such a value (the index stays clean), and building a query from such text (QueryBuilder) throws too.NGramTokenizerand thenGram/edgeNGramfactories fail the same way.
Users of the two tokenizers with maxGram = Integer.MAX_VALUE as "no upper limit", directly, via CustomAnalyzer or with maxGramSize=2147483647 in a factory configuration. Opt-in and rare. Short test strings pass, then every document or query text of 17 or more chars fails; nothing is corrupted. Workaround: a finite maxGram, or the n-gram token filters.
Mechanism, terms and numbers710 words
NGramTokenizer and EdgeNGramTokenizer (its subclass) accept maxGram = Integer.MAX_VALUE: init checks only minGram >= 1 and minGram <= maxGram. It then sizes the read buffer with CharacterUtils.newCharacterBuffer(2 * maxGram + 1024) and calls termAtt.resizeBuffer(2 * maxGram), both in int. For Integer.MAX_VALUE, 2 * maxGram wraps to -2: the read buffer gets 1022 chars, and resizeBuffer(-2) is a no-op, so the term buffer keeps its default size, 16 chars on a 64-bit JVM. With minGram = 1, texts of up to 16 chars tokenize correctly; the first text of 17 chars throws ArrayIndexOutOfBoundsException: Index 16 out of bounds for length 16 from incrementToken, and from 1022 chars (text without supplementary characters) an IllegalArgumentException from CharacterUtils.fill. The constructors, CustomAnalyzer and the nGram / edgeNGram factories (maxGramSize=2147483647) all reach it, at index time and at query time, while the n-gram token filters accept the same value and work. Introduced in 4.4.0 by LUCENE-5042 (2013); every release from 4.4.0 to 10.6.0 and main is affected. Opt-in and rare; documents are rejected one by one and nothing is corrupted.
- Accepted.
NGramTokenizer.initthrows only forminGram < 1andminGram > maxGram(NGramTokenizer.java:118-124).EdgeNGramTokenizer extends NGramTokenizer(EdgeNGramTokenizer.java:30) and passesmaxGramthrough; both factories readmaxGramSizewithgetIntand pass it on unchecked (EdgeNGramTokenizerFactory.java:48-49,NGramTokenizerFactory.java:50-51). - Read buffer wraps.
charBuffer = CharacterUtils.newCharacterBuffer(2 * maxGram + 1024)with the comment "2 * maxGram in case all code points require 2 chars and + 1024 for buffering to not keep polling the Reader" (:128-132).2 * 2147483647is -2 inint, so the size is 1022, which passes the only check,bufferSize < 2(CharacterUtils.java:39-43). The code-point buffer gets the same length (:133). - Term buffer never grows. "// Make the term att large enough",
termAtt.resizeBuffer(2 * maxGram)(:134-135), i.e.resizeBuffer(-2).CharTermAttributeImpl.resizeBuffergrows onlyif (termBuffer.length < newSize)(CharTermAttributeImpl.java:57-58), so the buffer keeps its initialArrayUtil.oversize(10, Character.BYTES)chars (:30-32): 16 on a 64-bit JVM (read back after construction in an executed probe), 14 on a 32-bit one (ArrayUtil.java:174-203). - Overflow.
gramSize > maxGram(:162) is never true, so grams grow with the text.CharacterUtils.toChars(buffer, bufferStart, gramSize, termAtt.buffer(), 0)(:184) writes the 17th char of the 17-char prefix to index 16 of the 16-char buffer. - From 1022 chars (text without supplementary characters). The compaction test at
:146is always true, so every call refills. After a first fill of 1022 one-char code points,buffer.length - bufferEndis 0, andCharacterUtils.fill(:154) rejects it: "numChars must be >= 2 and <= the buffer size" (CharacterUtils.java:177-178).
| maxGram | what happens |
|---|---|
| up to 1,073,741,311 | no wrap; about 16.5 bytes of buffers per unit of maxGram, so 10^6 works and 10^8 runs out of memory at construction with a 512 MB heap |
| 1,073,741,312 to 2,147,483,136 | 2 * maxGram + 1024 is below 2: the constructor throws IllegalArgumentException: buffersize must be >= 2 |
2,147,483,137 to 2,147,483,647 (511 values, Integer.MAX_VALUE among them) | accepted with a 2..1022-char read buffer and the default term buffer |
| text length | result |
|---|---|
| 1 to 16 chars | correct grams |
| 17 to 1021 chars | ArrayIndexOutOfBoundsException: Index 16 out of bounds for length 16 |
| 1022 chars or more | IllegalArgumentException: numChars must be >= 2 and <= the buffer size after the first gram |
Text of supplementary characters fails as soon as a gram passes 16 chars (Index 17 out of bounds for length 16, for example 9 emoji = 18 chars), also from 1022 chars, because its first fill holds only 511 code points.
Not part of the claim (contrived): with a large minGram (1023 or more, or 512 or more for text of supplementary characters) on a text longer than the read buffer, incrementToken reaches assert exhausted; return false (:163-165) before any token, so the stream is silently empty with assertions off (NGramTokenizer(1023, Integer.MAX_VALUE) on 1100 a: 0 tokens instead of 3081) and throws AssertionError with them on. The fix below rejects these settings too.
Exposure and likelihood283 words
- Who is hit. Users of the two tokenizers with
maxGram=Integer.MAX_VALUE(or one of the 510 values just below it), for example "all prefixes" withEdgeNGramTokenizer. All entry points reach it: the constructors,CustomAnalyzerwithedgeNGram/nGram, the factories'create(), and a schema withmaxGramSize=2147483647. - Default vs opt-in. Opt-in: the n-gram tokenizers are optional analysis-common components with defaults
maxGram= 2 (NGramTokenizer) and 1 (EdgeNGramTokenizer). - What goes wrong. The failure is deferred and depends on the data: a quick test with short strings passes, then every text of 17 chars or more throws from
incrementToken. At index timeaddDocumentthrows for that document, the writer stays open, later documents index andCheckIndexis clean; at query timeQueryBuilderthrows the same exception. WithminGram= 1 no output is silently wrong; only a contrived large-minGramsetting ends a stream early (see the full explanation above). - Harder to trace in a long-running JVM. With default JVM flags, after repeated failures the JVM's fast-throw optimisation drops the message and stack: later failures show as
ArrayIndexOutOfBoundsException: nullwith no Lucene frame. - Scale. None: 17 chars. Deterministic, single-threaded, assertions on or off.
- Workaround. A finite
maxGram(the longest text you expect), orEdgeNGramTokenFilter/NGramTokenFilter, which acceptInteger.MAX_VALUEand work. - How likely. Rare. The idiom exists: an old Solr geohash plugin builds
EdgeNGramTokenizer(input, Side.FRONT, 1, Integer.MAX_VALUE)on the pre-4.4 API, and its geohashes are 22 chars by default, so on 4.4.0 or later the same idiom would fail on every value. But the failure is loud on the first long value and the documented public searches found no report of it since 4.4.0 (2013).
One JUnit method on the public API, in the n-gram package like the upstream tests: EdgeNGramTokenizer(1, Integer.MAX_VALUE) on the 17-char text abcdefghijklmnopq. It passes if the constructor rejects the value or the 17 prefixes come out, so it accepts either fix. It fails with assertions on and off.
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.lucene.analysis.ngram;
import java.io.StringReader;
import org.apache.lucene.tests.analysis.BaseTokenStreamTestCase;
public class TestEdgeNGramTokenizerMaxGramIntegerMaxValue extends BaseTokenStreamTestCase {
public void testSeventeenCharText() throws Exception {
// expected: the 17 prefixes or an IllegalArgumentException; actual: ArrayIndexOutOfBounds
String text = "abcdefghijklmnopq";
String[] prefixes = new String[text.length()];
for (int i = 0; i < prefixes.length; i++) {
prefixes[i] = text.substring(0, i + 1);
}
EdgeNGramTokenizer tokenizer;
try {
tokenizer = new EdgeNGramTokenizer(1, Integer.MAX_VALUE);
} catch (IllegalArgumentException acceptable) {
return;
}
tokenizer.setReader(new StringReader(text));
try {
assertTokenStreamContents(tokenizer, prefixes);
} catch (ArrayIndexOutOfBoundsException e) {
throw new AssertionError(
"expected the 17 prefixes of " + text + " or an IllegalArgumentException, got " + e, e);
}
}
}
cp $REPRO/TestEdgeNGramTokenizerMaxGramIntegerMaxValue.java $LUCENE/lucene/analysis/common/src/test/org/apache/lucene/analysis/ngram/
cd $LUCENE && ./gradlew :lucene:analysis:common:test --tests org.apache.lucene.analysis.ngram.TestEdgeNGramTokenizerMaxGramIntegerMaxValue -Ptests.seed=DEADBEEF
- Expected
- The 17 prefixes
a...abcdefghijklmnopq, or anIllegalArgumentExceptionfrom the constructor. - Observed
- 16 correct prefixes, then
ArrayIndexOutOfBoundsException: Index 16 out of bounds for length 16fromNGramTokenizer.incrementToken.
More files, environment, full output258 words
Copy the test into lucene/analysis/common/src/test/org/apache/lucene/analysis/ngram/ of a Lucene checkout and run it with gradle (command below). 17 chars is the smallest failing input: 16 chars still work. With the fix it passes unchanged, so it doubles as the regression test.
The test logic has no random branch; it failed under every recorded seed, with assertions on and off. NGramTokenizer(1, Integer.MAX_VALUE), both factories, CustomAnalyzer, IndexWriter.addDocument and QueryBuilder fail the same way (executed with independent main() probes); they are left out of the test on purpose.
Executed: gradle on a clean 645d4fe14a20 (fails); the standalone runner on the stock main jars with -ea and -da and several seeds, and on the 10.6.0 and 10.5.2 release jars with -ea and -da (fails).
| lucene | main 645d4fe14a20 (11.0.0-SNAPSHOT): fails; also fails on the 10.6.0 and 10.5.2 release jars |
|---|---|
| jdk | OpenJDK 25.0.3 (build 25.0.3+9-LTS), 64-bit |
| os | Linux aarch64 (nothing platform-specific: it is int arithmetic) |
cp $REPRO/TestEdgeNGramTokenizerMaxGramIntegerMaxValue.java $LUCENE/lucene/analysis/common/src/test/org/apache/lucene/analysis/ngram/
cd $LUCENE && ./gradlew :lucene:analysis:common:test --tests org.apache.lucene.analysis.ngram.TestEdgeNGramTokenizerMaxGramIntegerMaxValue -Ptests.seed=DEADBEEF
# gradle, main 645d4fe14a20, -Ptests.seed=DEADBEEF (module prefixes and 5 test-framework frames trimmed)
TestEdgeNGramTokenizerMaxGramIntegerMaxValue > testSeventeenCharText FAILED
java.lang.AssertionError: expected the 17 prefixes of abcdefghijklmnopq or an IllegalArgumentException, got java.lang.ArrayIndexOutOfBoundsException: Index 16 out of bounds for length 16
at org.apache.lucene.analysis.ngram.TestEdgeNGramTokenizerMaxGramIntegerMaxValue.testSeventeenCharText(TestEdgeNGramTokenizerMaxGramIntegerMaxValue.java:41)
Caused by:
java.lang.ArrayIndexOutOfBoundsException: Index 16 out of bounds for length 16
at java.base/java.lang.Character.toChars(Character.java:9873)
at org.apache.lucene.analysis.CharacterUtils.toChars(CharacterUtils.java:145)
at org.apache.lucene.analysis.ngram.NGramTokenizer.incrementToken(NGramTokenizer.java:184)
...
at org.apache.lucene.analysis.ngram.TestEdgeNGramTokenizerMaxGramIntegerMaxValue.testSeventeenCharText(TestEdgeNGramTokenizerMaxGramIntegerMaxValue.java:39)
# standalone runner
main 645d4fe14a20 -ea / -da tests_run=1 failures=1 same ArrayIndexOutOfBoundsException at NGramTokenizer.java:184
10.6.0 -ea / -da tests_run=1 failures=1 same
10.5.2 -ea / -da tests_run=1 failures=1 same (CharacterUtils.toChars is line 111 there)- The javadoc says only "the largest n-gram to generate"; the constructor validates
maxGramand accepts this value. - The code sizes the buffers "in case all code points require 2 chars"; the n-gram token filters work with the same value.
- Robert Muir called
Integer.MAX_VALUEsize parameters "a trap" (#8756), which led to construction-time checks in other tokenizers.
The full argument335 words
- Contract.
@param maxGram the largest n-gram to generate(NGramTokenizer.java:91,:107;EdgeNGramTokenizer.java:38,:49). No upper bound and no note that large values fail, there, in the factories or inpackage-info. - Intent in the code. "2 * maxGram in case all code points require 2 chars" (
NGramTokenizer.java:130-132) and "Make the term att large enough" (:134): the wrap defeats both. - CHANGES. 4.4.0, LUCENE-3907: "EdgeNGramTokenizer now supports maxGramSize > 1024" (
lucene/CHANGES.txt:13581), with no upper limit. - Siblings and history.
EdgeNGramTokenFilterandNGramTokenFilteracceptInteger.MAX_VALUEand give the right grams (executed). On 4.3.1 both tokenizers give the right grams for the same 16- and 17-char texts (executed; that version read at most 1024 chars). - Nearby tokenizers. Several analysis tokenizers reject oversized length settings up front with an
IllegalArgumentException:KeywordTokenizerandCharTokenizerin their constructors (KeywordTokenizer.java:41-48,CharTokenizer.java:71) andStandardTokenizer.setMaxTokenLength(StandardTokenizer.java:95-99), againstMAX_TOKEN_LENGTH_LIMIT = 1024 * 1024(StandardTokenizer.java:80). - Maintainers (credit Robert Muir). On #8756 (LUCENE-7705), about size parameters of
CharTokenizerandKeywordTokenizer: "I thinkInteger.MAX_VALUEis a trap for a number of reasons: enormous values will likely only blow up anyway (limited to 32766 in the index), if they don't they may create hellacious threadlocal memory usage in buffers, bad/malicious input could take out JVMs, etc.", and "Maybe follow the example of StandardTokenizer here", which led toMAX_TOKEN_LENGTH_LIMIT. On #6621 (LUCENE-5559, argument validation), about whether factories need their own checks: "if the factory would crash in some way that is confusing on certain inputs, then it would need a check too"; here neither the factories nor the tokenizer check. On #9395 (LUCENE-8348, open), a cost remark that proposes documentation, not a check: "it seems to surprise people that min=small, max=huge eats up a ton of resources". - Tests. No upstream test uses a huge
maxGram(TestNGramTokenizerandTestEdgeNGramTokenizergo up to 1300).TestRandomChains.java:176keeps randomintarguments in -50..50 because the full range "could cause huge ram usage": about allocation, not a wrap.
The devil's-advocate check149 words
The strongest "works as designed" case (built by the intended-behaviour verifier): the buffers are sized from maxGram on purpose; the 4.4-4.10 EdgeNGramTokenizer javadoc warned that a maxGram larger than 1024 "will result in increased memory usage" (removed in 5.0); #9395 calls a huge max expensive; TestRandomChains avoids huge ints; configuration values are trusted (SECURITY.md:42). So Integer.MAX_VALUE can never be honoured as truly unbounded, and it fails loudly.
Why it fails: all of that is about cost, and the smaller huge values do fail fast at construction (OutOfMemoryError, or IllegalArgumentException: buffersize must be >= 2). As the adjudication put it: "That covers allocation cost or an OutOfMemoryError. It does not cover an accepted value wrapping into a tiny buffer and failing later with an internal-error exception." Nothing documents a limit. The adjudication kept the priority low and the confidence medium because the value is extreme.
Recommended Reject maxGram above an overflow-safe bound
Reject maxGram above (Integer.MAX_VALUE - 1025) / 3 = 715,827,540 in NGramTokenizer.init with an IllegalArgumentException (+10 lines; both tokenizers and both factories inherit it). The bound keeps the tokenizer's buffer index arithmetic within int. The minimal test, the format check and all 1910 analysis-common tests pass.
/**
* The largest accepted {@code maxGram}. The tokenizer buffers {@code 2 * maxGram + 1024} chars
* and indexes that buffer with int arithmetic; this bound keeps those indexes in int range.
*/
public static final int MAX_NGRAM_SIZE_LIMIT = (Integer.MAX_VALUE - 1025) / 3;
// in init(), after the minGram > maxGram check
if (maxGram > MAX_NGRAM_SIZE_LIMIT) {
throw new IllegalArgumentException(
"maxGram must not be greater than " + MAX_NGRAM_SIZE_LIMIT + ", got: " + maxGram);
}All options and trade-offs4 options · 714 words
Four options. Option A was validated with gradle in a scratch worktree at 645d4fe14a20; A', B and C were not executed. A rejecting fix (A, A', B) turns today's accidental success on texts of at most 16 chars into an error when the tokenizer is created; only C makes Integer.MAX_VALUE behave like maxGram = text length. A and A' also reject the contrived large-minGram settings that end a stream early.
Add a constant MAX_NGRAM_SIZE_LIMIT = (Integer.MAX_VALUE - 1025) / 3 (715,827,540) and, in init after the minGram > maxGram check, throw IllegalArgumentException("maxGram must not be greater than " + MAX_NGRAM_SIZE_LIMIT + ", got: " + maxGram). Why this bound: it keeps every buffer index sum within int even under a loose bound on the loop state (bufferStart <= bufferEnd <= 2 * maxGram + 1024, minGram <= maxGram). The largest such sum is bufferStart + 1 + minGram (NGramTokenizer.java:163), at most 3 * maxGram + 1025. The hunt's z3 model proves that the modelled sums (:130, :146, :162, :163, :195) cannot wrap up to this bound. It is a conservative choice, not a proven maximum, and it does not cover the stream char offsets (:188, :209), which depend on the stream length, not on maxGram.
/**
* The largest accepted {@code maxGram}. The tokenizer buffers {@code 2 * maxGram + 1024} chars
* and indexes that buffer with int arithmetic; this bound keeps those indexes in int range.
*/
public static final int MAX_NGRAM_SIZE_LIMIT = (Integer.MAX_VALUE - 1025) / 3;
// in init(), after the minGram > maxGram check
if (maxGram > MAX_NGRAM_SIZE_LIMIT) {
throw new IllegalArgumentException(
"maxGram must not be greater than " + MAX_NGRAM_SIZE_LIMIT + ", got: " + maxGram);
}Trade-offs
Integer.MAX_VALUEnow fails when the tokenizer is created (for anAnalyzer, when it first builds its components) instead of on the first long text; texts of at most 16 chars, which work by accident today, fail too.- Values just below the bound are still accepted and need about 12 GB of buffers per tokenizer (about 16.5 bytes per unit of
maxGram), so they fail withOutOfMemoryErrorunless the heap holds that, as today; the tests check the rejection, not that values near the bound are practical. - Adds one public constant (it could be private); no other behaviour changes.
What the checks validated
- Gradle, scratch worktree at
645d4fe14a20, OpenJDK 25.0.3,-Ptests.seed=DEADBEEF: without the fix the minimal test fails; with it:lucene:analysis:common:checkGoogleJavaFormatand the whole:lucene:analysis:common:testtask pass (340 suites, 1910 tests, 0 failures, 18 skipped), including the minimal test,TestNGramTokenizer14,TestEdgeNGramTokenizer15,TestNGramFilters13,TestNGramTokenFilter17,TestEdgeNGramTokenFilter17 andTestConditionalTokenFilter12. - Shadowed onto the main jars: the minimal test passes with
-eaand-da; the query probe and an independent probe getmaxGram must not be greater than 715827540, got: 2147483647for everyInteger.MAX_VALUEcase (both tokenizers,CustomAnalyzer, theIndexWriterand query paths) and for every boundary value from 1,073,741,312 up, whilemaxGram= 17, 1,000,000 and the token-filter control are unchanged.
Use StandardTokenizer.MAX_TOKEN_LENGTH_LIMIT (1024 * 1024) as the bound, as KeywordTokenizer and CharTokenizer do, and document it on the constructors.
Trade-offs
Matches those tokenizers and also removes most of the out-of-memory regime, but rejects values from 1,048,577 to 715,827,540, which work today when the heap is large enough. A maintainer's call. Not executed (the minimal test accepts it unchanged).
Compute the sizes with Math.multiplyExact / Math.addExact in init and rethrow an ArithmeticException as IllegalArgumentException.
Trade-offs
Rejects exactly the values whose sizes wrap (from 1,073,741,312); 715,827,541 to 1,073,741,311 stay accepted (they need more than 11 GB of buffers) and are outside the model's index-overflow proof. Not executed.
Size the buffers from a small value and grow the term buffer before the copy at NGramTokenizer.java:184 (and the read buffer when a gram needs more).
Trade-offs
The only option that makes Integer.MAX_VALUE behave like maxGram = text length, as the token filters do, but it changes the streaming loop and its memory profile: out of proportion for a rare setting. Not executed.
A product that lets its users define analyzers should cap maxGram itself: with this bug, one such setting makes every document or query text of 17 or more chars fail.
A probe of numeric limits ran analysis components with size parameters far above the input length and compared them with the size set to the input length: the two n-gram tokenizers failed at Integer.MAX_VALUE, while the n-gram token filters passed. A z3 model of the int arithmetic then gave the exact range of affected values.
The method in full219 words
Method: executed differential testing, then a formal model.
- First signal. A planning probe in a hunt round on numeric limits of public API arguments ran
NGramTokenizer(1, n)andEdgeNGramTokenizer(1, n)on a 28-char text againstmaxGram= text length, for boundary values ofn:ArrayIndexOutOfBoundsExceptionfor2^31-1and2^31-2,IllegalArgumentExceptionfor2^30,OutOfMemoryErrorfor 1,073,741,224 with a 1 GiB heap. The n-gram token filters,ShingleFilterandLimitTokenCountFilterpassed as controls. - Model. The finder modelled
init's sizing and the index arithmetic ofincrementTokenin z3 with exact 32-bit wrap (21 queries): the exact partition of allmaxGramvalues, and a bound (715,827,540) under which none of the modelled index sums can wrap. - Translation validation. A bounded-exhaustive comparison of the real jar with an exact reference n-gram semantics (8.7 million tokenizations,
minGram1 to 3 and nearmaxGram). ForInteger.MAX_VALUEwithminGram1 to 3 it found no silently wrong output. Its silent results forInteger.MAX_VALUEall haveminGram=Integer.MAX_VALUE - 1on a text longer than the 1022-char read buffer: no tokens, as expected, but a final offset of 1022 instead of the text length (anAssertionErrorwith assertions on). That is the contrived large-minGramcorner described above, not claimed. - Reduction. A public-API JUnit test, later reduced to the single test shown here.
No prior report found (searched 2026-10-29 and twice on 2026-10-30) in GitHub issues, PRs and commits including the JIRA archive, the mailing lists, downstream trackers or the web. Related only: #9395 (a huge max is expensive) and #8756 (the Integer.MAX_VALUE "trap" in other tokenizers).
Searches4 searches
Related, not this bug: #9395 (LUCENE-8348, open: "min=small, max=huge eats up a ton of resources"), #8756 (LUCENE-7705: Integer.MAX_VALUE size parameters called a trap, which led to MAX_TOKEN_LENGTH_LIMIT in CharTokenizer and KeywordTokenizer), #13802 and the stale open PR #13814 (EdgeNGramTokenizer's default max gram size), #6106 (LUCENE-5042, which introduced the sizing and never discussed large values), #6621 (LUCENE-5559: lower-bound checks only), #5942 (LUCENE-4877: factories reject unknown parameters, not out-of-range values), #9009, #4980, #2304, #1834 (filter options, earlier rewrites and the old 1024-char clipping); downstream es#25841 (an ArrayIndexOutOfBoundsException for max_gram 6000 in TermsHashPerField, a different root cause), es#146497 and OpenSearch #23035 (gram-range limits and parsing). No open PR changes the sizing. The tracking issue and mailing-list thread do not mention n-grams, so there is no maintainer triage. Coverage limits: GitHub search is token-based, so exact-phrase matches on the error strings are approximate; private reports are not visible.
| Where | When | How | Found |
|---|---|---|---|
| Search tool (GitHub issues/PRs/commits incl. the JIRA archive, mailing lists, JIRA, downstream, web) | 2026-10-29; 2026-10-30 (twice) | 51 to 61 queries per run: class and parameter names, both error texts, overflow and Integer.MAX_VALUE phrasings | no hit about this bug (latest run: 469 hits, 0 high) |
| GitHub apache/lucene (manual) | 2026-10-29 and 2026-10-30 | 36 queries: class names with maxGram, MAX_VALUE, 2147483647, overflow, out of bounds, both error strings, newCharacterBuffer, MAX_TOKEN_LENGTH_LIMIT, items since 2026-10-29; 18 related issues and 2 JIRA tickets read in full | #9395 and #8756 remarks only |
| Open PRs | 2026-10-29 and 2026-10-30 | changed files of all open apache/lucene PRs (408 on 2026-10-30) | only #13814 touches the n-gram tokenizers (default constant); #13390 edits an n-gram filter test and #14389 CharacterUtils case folding; none changes the sizing |
| Downstream, JIRA, mailing lists, web | 2026-10-30 | 18 queries over Elasticsearch, OpenSearch, Solr, the archived lucene-solr repo and Lucene.NET; 13 JIRA queries; 42 list queries; 11 web queries; 9 Stack Overflow searches | resource-limit and parsing items only |
Introduced in 4.4.0 by LUCENE-5042 (2013), which doubled the sizing to 2 * maxGram + 1024, added resizeBuffer(2 * maxGram) and made EdgeNGramTokenizer a subclass of NGramTokenizer. 4.3.1 passes; 13 releases from 4.4.0 to 10.6.0 and main fail when run, and every release tag from 4.4.0 to 10.6.0 has the sizing.
Versions tested7 rows
Introducing commits: e021451d6c0 (trunk, svn r1492185) and the branch_4x backport e6ce05111fe (svn r1492231), 2013-06-12, Adrien Grand, "LUCENE-5042: Fix the n-gram tokenizers and filters." (JIRA LUCENE-5042, now #6106; SVN era, no PR). The backport shadowed over the 4.4.0 jars reproduces the bug for both tokenizers. At its parent 0b198a431f4, NGramTokenizer throws NegativeArraySizeException in its constructor (from the earlier, never separately released new char[maxGram + 1024], LUCENE-4955), and EdgeNGramTokenizer, then a separate class with its own buffer, gives the right grams. In 4.4.0-5.5.5 only the current-version path is affected: factories with luceneMatchVersion < 4.4 built the unaffected Lucene43* tokenizers, removed in 6.0. The sizing is in all 125 upstream release tags from 4.4.0 to 10.6.0 (the 115 in the history clone, the nine 8.8.2-8.11.4 tags it lacks, read through the GitHub API, where NGramTokenizer.java is byte-identical in all nine, and 10.6.0), and in none of 2.1.0 to 4.3.1. No later change touched it; the last commit on the two tokenizers is 8ef6a0da568 (2020-12-28, reformatting). Old releases ran on JDK 25.
| Version or commit | Verdict | Result |
|---|---|---|
main 645d4fe14a20 | affected | the minimal test fails (gradle; standalone -ea and -da) |
| 10.6.0, 10.5.2 | affected | the minimal test fails (-ea, -da); probe: 16 chars correct, 17 chars ArrayIndexOutOfBoundsException |
| 4.4.0 to 10.0.0 | 11 versions | a portable probe fails on 4.4.0, 4.10.4, 5.0.0, 5.5.5, 6.0.0, 6.6.6, 7.7.3, 8.11.4, 9.0.0, 9.12.3 and 10.0.0: 16 chars correct, 17 chars ArrayIndexOutOfBoundsException |
e6ce05111fe (introducing backport) | affected | reproduces over the 4.4.0 jars, both tokenizers |
0b198a431f4 (its parent) | fails loudly | NegativeArraySizeException in the NGramTokenizer constructor; EdgeNGramTokenizer correct |
| 4.3.1 | not affected | both tokenizers give the right grams for 16 and 17 chars (-ea, -da) |
fix A (scratch worktree at 645d4fe14a20) | not affected | the minimal test and all 1910 analysis-common tests pass |
An independent reproducer, a root-cause review by a model from another family, an intended-behaviour devil's advocate, an adjudication, a credibility audit, a current-main reproducer, a tie-breaker and two rounds of fact checks all confirm it; this doc re-ran the test with gradle, ran the query path and validated the fix on the whole analysis-common test task.
Checks and verdicts818 words
All cross-model reviews ran reading only and could not run Java. The refute lens returned a procedural "not real" ("a procedural verdict, not a static refutation") while agreeing on the root cause and that the inputs are in contract; the tie-breaker then ran what it asked for and confirmed the bug. The fact check of the previous doc found seven text errors (the large-minGram corner, the fix-bound rationale, the plugin's 22-char values, an unfounded judge attribution, the 1022-char threshold's qualifier, the open-PR result and a misattributed cross-model review check); the check from another model family added the 64-bit qualifier of the 16-char buffer and the parent-commit wording, among other caveats. A second round of both checks found that the doc misstated the scope of the finder's translation validation and listed the wrong earlier errors, and asked for the supplementary-character qualifier on the 1022-char threshold everywhere and narrower wording on novelty, nearby tokenizers and the query path. All are addressed here. No executing cross-model review exists.
| Check | Verdict | What it found |
|---|---|---|
| finder (z3 model and translation validation) | candidate | z3 partition of all maxGram values (21 queries); 8.7 million tokenizations compared with a reference; JUnit fails 4 of 4 on main (-ea, -da), 10.5.2 and 9.12.3; with its fix 4 of 4 pass and 42 upstream n-gram tests pass. |
| cross-model reviews, refute and intended lenses (OpenAI GPT-5.6, reading only) | procedural "not real" / uncertain | Could not run Java; agreed on the root cause and that the inputs are in contract; called the intended-behaviour case weak. |
| independent reproducer | real | Own 6-test JUnit: its 3 Integer.MAX_VALUE tests fail and its 3 controls pass on main (-ea, -da), 10.5.2 and 9.12.3; a boundary probe confirms the partition. |
| root-cause review (OpenAI GPT-5.6, reading only) and the verifier's own check | real (high) | The review: both wraps (1022 and -2), the fix fixes it, inputs in contract. Separately, the verifier's own 32-bit and public-API check on the stock jars (not part of the review) matches. |
| intended-behaviour devil's advocate | real | Built the works-as-designed case (memory warnings, #9395, TestRandomChains) and rejected it; regime probe: 10^6 works, 10^8 out of memory, 2^30 rejected, Integer.MAX_VALUE accepted then fails at 17 chars. |
| adjudication | confirmed (medium) | Re-ran both JUnits (-ea, -da, a new seed); medium because of the no-noise risk; restricted the claim to Integer.MAX_VALUE. |
| Registration check and credibility audit | keep | Single-test re-run fails on main and passes with the fix; the audit re-ran the finder's JUnit with a new seed (-da): 4 of 4 fail. |
| minimal-repro audit | verified | The one test fails on main (-ea, -da) and passes with the fix. |
| current-main reproducer (two runs) | real | The one test fails on main and 10.5.2 (-ea, -da) and passes with the fix; not fixed upstream. |
| tie-breaker (two runs) | real (high) | The one test on main and 10.5.2 (-ea, -da); an independent main() probe reading the 16-char term buffer after construction, with controls, NGramTokenizer, CustomAnalyzer, the 1022-char case, the construction boundary, the IndexWriter path and default JVM flags. |
| history check | since 4.4.0 | 4.3.1 passes; 4.4.0, 6.0.0, 10.6.0 and main fail; the introducing backport reproduces, its parent does not. |
| first fact check (previous doc) | bug real; 7 text errors (fixed) | Re-ran the gradle repro, the fix on the whole module, probes on main, 10.6.0, 10.5.2 and 4.10.4 to 10.0.0, the factories and the large-minGram and supplementary cases. |
| first fact check of the brief (OpenAI GPT-5.6, reading only) | bug real; caveats (addressed) | Asked to qualify the 16-char buffer (64-bit JVM), minGram = 1, the parent-commit wording and the query path, which this doc now executes. |
| second fact check (this doc's previous revision) | bug real; 2 text errors (fixed here) | Re-ran the gradle repro (assertions on and off), fix A on the whole module, repro.sh with a new seed on main, 10.6.0 and 10.5.2, and its own probes on main, 4.3.1, 4.4.0, 8.11.4 and 9.12.3. Found the misstated translation-validation scope and the wrong list of earlier errors. |
| second fact check of the brief (OpenAI GPT-5.6, reading only) | bug real; 1 error, wording (fixed here) | Asked for the supplementary-character qualifier on the 1022-char threshold in the brief's impact section, and narrower wording on the upstream-status citation, release coverage, novelty, nearby tokenizers and the query path. |
| this doc | gradle repro, query path, fix | The minimal test with gradle on a clean 645d4fe14a20 (fails) and with fix A (passes; format check and the whole analysis-common test task); the query path on main (fails) and with the fix (rejected at construction). This revision re-ran the finder's translation validation for Integer.MAX_VALUE only (unchanged harness, -ea and -da) and read the 8.8.2-8.11.4 release tags, which the history clone lacks, through the GitHub API. |
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 515 KB
Finding record, Lucene and upstream sources, repro files and diagram notes1,589 words
- Registry title
- NGramTokenizer and EdgeNGramTokenizer accept maxGram = Integer.MAX_VALUE, but their int buffer sizing wraps to a 1022-char read buffer and leaves the 16-char default term buffer, so the first text of 17 or more chars throws (ArrayIndexOutOfBoundsException up to 1021 chars, an unrelated IllegalArgumentException from 1022)
- Area
- analysis-common / n-gram tokenizers (NGramTokenizer.init buffer sizing, shared by EdgeNGramTokenizer; NGramTokenizerFactory and EdgeNGramTokenizerFactory pass maxGramSize through unchecked)
- Priority
- Low, rank 642: NGramTokenizer and EdgeNGramTokenizer are widely used but maxGram = Integer.MAX_VALUE is a rare setting and fails loudly without writing anything wrong.
- Impact class
- crash
- Impact note
- Applications on any release from 4.4.0 to 10.6.0 or main that build an NGramTokenizer or EdgeNGramTokenizer (directly, through CustomAnalyzer, or through NGramTokenizerFactory/EdgeNGramTokenizerFactory with maxGramSize=2147483647 in a schema) with maxGram = Integer.MAX_VALUE as 'no upper limit' get a tokenizer that the constructor accepts and that handles texts of up to 16 chars correctly, but every document or query text of 17 or more chars then throws from incrementToken (ArrayIndexOutOfBoundsException 'Index 16 out of bounds for length 16' for 17 to 1021 chars, IllegalArgumentException 'numChars must be >= 2 and <= the buffer size' from 1022 chars), so such documents are rejected at index time (a non-aborting per-document failure) and such queries fail; the index is not damaged, and maxGram values from 1,073,741,312 to 2,147,483,136 already fail at construction.
- Summary
- You build EdgeNGramTokenizer(1, Integer.MAX_VALUE) (or NGramTokenizer, or a factory with maxGramSize=2147483647) to mean 'all n-grams, no upper limit', and you expect the same tokens as with maxGram = text length, or else a clear IllegalArgumentException from the constructor, which validates maxGram and accepts it. Instead init computes the buffer sizes 2 * maxGram + 1024 and 2 * maxGram in int, which wrap to 1022 and -2, so the tokenizer gets a 1022-char read buffer and keeps its 16-char default term buffer. Texts of up to 16 chars come out right, but the first text of 17 or more chars throws ArrayIndexOutOfBoundsException 'Index 16 out of bounds for length 16' from incrementToken (from 1022 chars, an IllegalArgumentException about a buffer size the caller never passed).
- Introduced
- e021451d6c08433d2e442456b3b5925f4cdb7129 (trunk) / e6ce05111fe7c22b871fb87901d23970bc6b10b3 (branch_4x backport, the released code): LUCENE-5042 'Fix the n-gram tokenizers and filters' (Adrien Grand; JIRA issue now apache/lucene#6106; SVN era, no PR) [read: git log -S and git blame -w on the three sizing expressions in the full-history clone] [exec: e6ce05111fe's NGramTokenizer, EdgeNGramTokenizer and CharacterUtils compiled over the 4.4.0 jars reproduce the bug; its parent 0b198a431f4 does not: there NGramTokenizer throws NegativeArraySizeException at construction from LUCENE-4955's 'new char[maxGram + 1024]' (a03e38d5d05, never released on its own) and EdgeNGramTokenizer had its own growing buffer]. LUCENE-5042 doubled the factor to 2 * maxGram + 1024, so the top 511 values wrap back to a small positive size, made termAtt.resizeBuffer(2 * maxGram) a no-op for maxGram >= 2^30, and made EdgeNGramTokenizer a subclass of NGramTokenizer. Not a regression of a later change. (2013-06-12)
- First affected version
- 4.4.0 [exec: fails with -ea and -da]; 4.3.1, the last release before it, passes [exec: -ea and -da; the old algorithm read at most 1024 chars and had no maxGram-sized buffer]
- Affected versions
- every release from 4.4.0 to 10.6.0 (the 10.6 RC, branch_10_6 4ae946e5a9b) and main 645d4fe14a20 [exec: 4.4.0, 4.10.4, 5.0.0, 5.5.5, 6.6.6, 7.7.3, 8.11.4, 9.0.0, 9.12.3, 10.0.0, 10.5.2, the 10.6 RC (core and analysis-common jars) and main all fail with the same ArrayIndexOutOfBoundsException 'Index 16 out of bounds for length 16' for both tokenizers (follow-up: a release-portable main() per API era, -da everywhere, -ea also on 4.4.0, 9.12.3 and 10.5.2); the finder's JUnit fails 4 of 4 on main (-ea, -da), 10.5.2 and 9.12.3; the independent reproduction's fails its 3 Integer.MAX_VALUE tests and passes its 3 controls on main, 10.5.2 and 9.12.3 (-ea, -da)] [read: the sizing expressions are in NGramTokenizer.java of all 115 release tags 4.4.0..10.5.2 and absent in 4.0.0..4.3.1]; in 4.4.0-5.5.5 only on the current-version path (factories with luceneMatchVersion < 4.4 used the unaffected Lucene43NGramTokenizer/Lucene43EdgeNGramTokenizer, removed in 6.0) [read]; not affected: 2.1.0-4.3.1 [read; 4.3.1 exec passes]; not fixed upstream (NGramTokenizer.java and EdgeNGramTokenizer.java last changed by 8ef6a0da568, author date 2020-12-28, reformatting only; upstream main head is 645d4fe14a20, author date 2026-10-28; no open PR changes the sizing). JDK 25.0.3 for all versions.
- 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 “not real” is procedural), an adjudication that ruled it real, an intended-behaviour check, a prior-report search and a skeptical audit; not yet triaged by a person.
- Found by
- non-formal; also reached by Z3, exhaustive check
- Upstream and prior reports
- none found (2026-10-29, read-only; the prior-report record, verdict new, plus the finder's runs and the independent reproduction's, the intended-behaviour check's and the adjudication's searches): the scripted prior-report search (51 queries: 16 GitHub issue/PR, 3 commit, 2 downstream, 20 mailing-list over dev@, java-user@, issues@ and commits@, 4 JIRA-archive and 6 web; 104 distinct hits, 0 high-overlap; the finder's earlier run with 51 other queries: 1205 hits, 0 high), 16 manual apache/lucene GitHub searches, every apache/lucene PR created since 2026-10-26 and issue created since 2026-10-25, the changed files of all 415 open PRs (none changes the sizing; only #13814, #13390, #14389 and #16911 touch ngram/ or CharacterUtils at all), the Solr, Elasticsearch and other downstream trackers, the LUCENE and SOLR JIRA archive and the Solr user lists. Recheck (2026-10-29): no apache/lucene item mentioning NGramTokenizer, EdgeNGramTokenizer, maxGram or ngram was updated since 2026-10-27; none of the 13 PRs and 10 issues created since 2026-10-27 (up to #16938) concerns n-grams; the newest commit on NGramTokenizer.java and EdgeNGramTokenizer.java is still 8ef6a0da568 (2020-12-28); upstream main head is 645d4fe14a20. Closest items, not reports: #9395 (LUCENE-8348, open: a maintainer notes that min=small, max=huge 'eats up a ton of resources'; a cost remark), #8756 (LUCENE-7705: a maintainer called Integer.MAX_VALUE size parameters a trap, which led to the CharTokenizer/KeywordTokenizer MAX_TOKEN_LENGTH_LIMIT; other classes), #13802 and the stale open PR #13814 (EdgeNGramTokenizer's default max gram size of 1), #6621 (LUCENE-5559, validation of negative values), #6106 (LUCENE-5042, which introduced the sizing and never discussed large values) and #4980 (LUCENE-3907); downstream elastic/elasticsearch#25841, #25887 and #146497 (n-gram resource limits, not this wrap).
- Lucene commit tested
- main
645d4fe14a20(11.0.0-SNAPSHOT): the minimal test with gradle in a clean scratch worktree (fails) and with fix A (passes, with the format check and the whole analysis-common test task: 340 suites, 1910 tests, 0 failures); the standalone runner with-eaand-da(fails; passes with the fix); the 10.6.0 and 10.5.2 release jars (fails). A version-portable probe on 16- and 17-char texts: 4.3.1 passes; 13 releases from 4.4.0 to 10.6.0 and main fail. Upstream main2f424fd428d(2026-10-30) has not changed the n-gram package,CharacterUtilsorCharTermAttributeImpl.
- Finding record
lucene/analysis/common/src/java/org/apache/lucene/analysis/ngram/NGramTokenizer.java:87-112 (at 645d4fe14a20) (constructor javadoc '@param maxGram the largest n-gram to generate', lines 91 and 107), :118-124 (init validates only minGram >= 1 and minGram <= maxGram), :128-133 (CharacterUtils.newCharacterBuffer(2 * maxGram + 1024) in int, with the comment on surrogate pairs and buffering, and the int buffer of the same length), :135 (termAtt.resizeBuffer(2 * maxGram)), :163-164 ('assert exhausted; return false'), :184 (CharacterUtils.toChars into termAtt.buffer(): the ArrayIndexOutOfBoundsException site)lucene/analysis/common/src/java/org/apache/lucene/analysis/ngram/EdgeNGramTokenizer.java:30 (at 645d4fe14a20) ('extends NGramTokenizer'), :34-53 (both constructors pass maxGram to NGramTokenizer with edgesOnly = true; javadoc 'the largest n-gram to generate' at 38 and 49)lucene/analysis/common/src/java/org/apache/lucene/analysis/ngram/EdgeNGramTokenizerFactory.java:45-53 (at 645d4fe14a20) and NGramTokenizerFactory.java:47-55 (maxGramSize read with getInt and passed through unchecked)lucene/core/src/java/org/apache/lucene/analysis/CharacterUtils.java:36-44 (at 645d4fe14a20) (newCharacterBuffer: 'buffersize must be >= 2'), :174-179 (fill: 'numChars must be >= 2 and <= the buffer size')lucene/core/src/java/org/apache/lucene/analysis/tokenattributes/CharTermAttributeImpl.java:30-32 (at 645d4fe14a20) (initial term buffer oversize(10, 2) = 16 chars), :57-64 (resizeBuffer grows only when length < newSize, so -2 is a no-op)lucene/analysis/common/src/java/org/apache/lucene/analysis/core/KeywordTokenizer.java:41-47 (at 645d4fe14a20) (the module's convention: an oversized size is rejected with IllegalArgumentException at construction)lucene/CHANGES.txt:13581 (at 645d4fe14a20) (4.4.0, LUCENE-3907: 'EdgeNGramTokenizer now supports maxGramSize > 1024', no limit stated)lucene/analysis.tests/src/test/org/apache/lucene/analysis/tests/TestRandomChains.java:176 (at 645d4fe14a20) (TODO: full int range 'could cause huge ram usage'; about allocation, not a wrap)lucene/core/src/java/org/apache/lucene/index/IndexingChain.java:1987-1994 (at 645d4fe14a20) (an analyzer exception is non-aborting; the document is marked deleted)SECURITY.md:28, :38, :42 ('configuration values are trusted as given'), :48-50 (in-scope reports)
- The finding in 30 seconds
a Lucene main checkout (645d4fe14a20) lucene/analysis/common/src/java/org/apache/lucene/analysis/ngram/NGramTokenizer.java:62-64, :91, :107, :118-136, :146-154, :162-165, :184a Lucene main checkout (645d4fe14a20) lucene/analysis/common/src/java/org/apache/lucene/analysis/ngram/EdgeNGramTokenizer.java:30-31, :38, :40-42, :49, :51-53; EdgeNGramTokenizerFactory.java:48-49, :62; NGramTokenizerFactory.java:50-51, :68a Lucene main checkout (645d4fe14a20) lucene/core/src/java/org/apache/lucene/analysis/CharacterUtils.java:39-43, :139-147, :174-178a Lucene main checkout (645d4fe14a20) lucene/core/src/java/org/apache/lucene/analysis/tokenattributes/CharTermAttributeImpl.java:30-32, :57-66; lucene/core/src/java/org/apache/lucene/util/ArrayUtil.java:174-203 (64-bit vs 32-bit rounding)
- Who is affected
GitHub dsmiley/SOLR-2155 src/main/java/solr2155/solr/schema/GeoHashField.java:69, :81 and src/main/java/solr2155/lucene/spatial/geohash/GridNode.java:51 (read-only GitHub API, 2026-10-30: the idiom, default length 22)a Lucene main checkout (645d4fe14a20) lucene/analysis/common/src/java/org/apache/lucene/analysis/ngram/NGramTokenizer.java:63-64; EdgeNGramTokenizer.java:31-32 (defaults)
- Why it looks unintended
a Lucene main checkout (645d4fe14a20) lucene/analysis/common/src/java/org/apache/lucene/analysis/ngram/NGramTokenizer.java:91, :107, :118-124, :128-135; EdgeNGramTokenizer.java:38, :49a Lucene main checkout (645d4fe14a20) lucene/CHANGES.txt:13581a Lucene main checkout (645d4fe14a20) lucene/analysis/common/src/java/org/apache/lucene/analysis/core/KeywordTokenizer.java:41-48; lucene/analysis/common/src/java/org/apache/lucene/analysis/util/CharTokenizer.java:71; lucene/core/src/java/org/apache/lucene/analysis/standard/StandardTokenizer.java:80, :95-99a Lucene main checkout (645d4fe14a20) lucene/analysis.tests/src/test/org/apache/lucene/analysis/tests/TestRandomChains.java:176GitHub apache/lucene #8756 (LUCENE-7705), comment by Robert Muir (read with gh issue view, 2026-10-30)
- Suggested fix
a Lucene main checkout (645d4fe14a20) lucene/analysis/common/src/java/org/apache/lucene/analysis/ngram/NGramTokenizer.java:146, :162-163, :175, :184, :188, :195, :209a Lucene main checkout (645d4fe14a20) lucene/core/src/java/org/apache/lucene/analysis/standard/StandardTokenizer.java:80
- TestEdgeNGramTokenizerMaxGramIntegerMaxValue.java (lucene/analysis/common, package org.apache.lucene.analysis.ngram, 1 test):
repro/LFV-1007/src/org/apache/lucene/analysis/ngram/TestEdgeNGramTokenizerMaxGramIntegerMaxValue.java(45 lines)
Made in parallel with this doc from the code at 645d4fe14a20, the minimal repro (the 17-char text) and an executed probe on the stock main jars (assertions on and off) and with the fix.
assets/LFV-1007/bug.png(515 KB)
