TeluguAnalyzer runs IndicNormalizationFilter, then TeluguNormalizationFilter, then its StopFilter. The normalizer shortens the long vowel signs ీ ూ ే ో to ి ు ె ొ, but the default list holds only the long spellings and the stop filter is an exact lookup, so in the stock analyzer 13 entries can never match a token.
TeluguAnalyzer: the normalizer shortens ూ to ు before the stop filter, so the filter looks up కుడా, misses the list entry కూడా, and the stemmer indexes కుడ; on the right, the 13 kept entries, the effect and the fix. Select the still to open it at full size. Generated with AI, checked against the code.- You index Telugu text with the stock
new TeluguAnalyzer(), for example "రాముడు కూడా వచ్చాడు" (roughly "Ramudu also came") and "రాముడు వచ్చాడు" ("Ramudu came"). కూడా ("also") is in the analyzer's default stop list. - You expect కూడా to be dropped: analyzing it gives no token, and the AND query "రాముడు కూడా" matches both documents, like "రాముడు" alone.
- Before the stop filter, the Telugu normalizer shortens the long vowel sign ూ to ు, so the filter looks up కుడా. The list holds only కూడా, nothing matches, and the stemmer indexes the term
కుడ. - The AND query becomes
+f:రాము +f:కుడand matches 1 of the 2 documents. 13 of the 43 single-word default stop words survive the same way, among them అయితే, మళ్ళీ, మాత్రమే and ఇప్పటికే; గురించి, which has no long vowel sign, is removed. - No error, with assertions on or off. Hindi's analyzer also runs its stop filter after its normalizers, but its list holds the normalized spellings too, so it removes all of its stop words.
Every application that analyzes Telugu with the stock new TeluguAnalyzer(), at index and query time, since 8.10.0. 13 default stop words become ordinary terms: extra postings and scoring noise, stricter AND and phrase queries, and stem collisions (కూడా and కుడి, "right", both become కుడ). Plain OR queries still find their documents; a fix needs a reindex.
Mechanism, terms and numbers584 words
TeluguAnalyzer normalizes every token before its StopFilter: IndicNormalizationFilter, then TeluguNormalizationFilter, then StopFilter, then TeluguStemFilter. The Telugu normalizer shortens the four long vowel signs ీ ూ ే ో to ి ు ె ొ, but the default stop list te/stopwords.txt holds only the original spellings, and StopFilter is an exact set lookup. So the filter sees కుడా while the list holds కూడా: 13 of the 43 single-word entries (30%), among them కూడా ("also"), అయితే, మళ్ళీ, మాత్రమే and ఇప్పటికే, are never removed and are indexed and searched as ordinary terms, with no error. HindiAnalyzer has the same chain shape, and its list ships the normalized forms, so it removes all of its single-word entries. The list has been like this since the analyzer was added (LUCENE-10083, 8.10.0); every release from 8.10.0 to 10.6.0 (10.6.0 by reading) and main is affected. The fix is a 16-line data change in the Hindi style; it changes indexed terms, so affected fields need reindexing.
- Stop words: common words an analyzer drops before indexing and querying.
TeluguAnalyzer()"Builds an analyzer with the default stop words" fromte/stopwords.txt(TeluguAnalyzer.java:98-101,:42-43). - Vowel sign (matra): the mark a Telugu consonant carries for its vowel. ూ (U+0C42) is the long u sign, ు (U+0C41) the short one; కూడా is క + ూ + డ + ా.
- The chain (
TeluguAnalyzer.java:114-125):StandardTokenizer->DecimalDigitFilter->SetKeywordMarkerFilter(only with a stem-exclusion set) ->IndicNormalizationFilter(:120) ->TeluguNormalizationFilter(:121) ->StopFilter(result, stopwords)(:122) ->TeluguStemFilter(:123).
TeluguNormalizerfolds the long vowel signs to the short ones on every token that is not marked as a keyword: ీ -> ి, ూ -> ు, ే -> ె, ో -> ొ (TeluguNormalizer.java:76-88, "// long -> short vowels matras"). The default constructor adds no keyword marker.StopFilter.accept()is an exactCharArraySetlookup of the current term (StopFilter.java:96-97).te/stopwords.txthas only the header# Telugu stop word listand the original spellings. Every entry with one of those four vowel signs is therefore unreachable in the stock analyzer (no stem-exclusion set): the filter sees కుడా, the set holds కూడా. The word goes on to the stemmer and is indexed asకుడ.
The file has 50 entry lines, 45 distinct entries (5 duplicate lines), 2 of them two words (lines 31 and 45, which can never match one token). Of the 43 single-word entries, 13 (30%) are never removed: మెచ్చుకో, చేయగలిగింది, ఇప్పటికే, మళ్ళీ, అయితే, కూడా, అందుబాటులో, వేరుగా, మాత్రమే, అందరూ, దూరంగా, చుట్టూ, వ్యతిరేకంగా (lines 2, 11, 12, 14, 18, 20, 21, 22, 37, 39, 43, 50, 51). Each differs from its normalized form only in those vowel signs. The entry గురించి has none of them and is removed.
- Words named in a stem-exclusion set: the keyword marker makes
TeluguNormalizationFilterskip them, so the raw entry matches (IndicNormalizationFilterignores the marker, but it changes none of the 13 entries). normalize()(TeluguAnalyzer.java:127-133, used for prefix, wildcard and fuzzy terms): it has noStopFilter.- Custom stop sets that hold the normalized forms.
A sweep ran every default entry of the 39 stock analysis-common analyzers that have a default stop set through the whole analyzer. Read entry by entry, the leftovers outside Telugu and BengaliAnalyzer are comment lines, hyphenated, elided or multi-word entries, so only these two lose single-word entries to pre-stop normalization (13 each; the Bengali case is a separate, already publicly described bug).
Exposure and likelihood338 words
- Who is hit. The default constructor of a stock language analyzer, so every Telugu field that uses it, at index and query time, on every release from 8.10.0. Also code that reuses
TeluguAnalyzer.getDefaultStopSet()after the same two normalization filters. Only Telugu fields use this analyzer. No dedicated Elasticsearch or OpenSearch Telugu analyzer was found (the Elasticsearch request #84800 is open), but Solr can configure a Lucene analyzer class by name (<analyzer class="...">, built with its no-arg constructor), so Solr users can use it directly. - What goes wrong (5-document probe index, stock analyzer vs the fixed list, assertions on and off):
- the 13 words are indexed: extra postings, scoring noise, and no position gap where a stop word should leave one;
- a query made only of them still matches: the term query కూడా matches 2 documents, where the fixed analyzer builds no query at all;
- AND and phrase queries that contain one are stricter: MUST("రాముడు కూడా") matches 1 document instead of 3, the phrase "రాముడు కూడా వచ్చాడు" 1 instead of 2;
- the stemmer folds the kept word onto an unrelated one: కూడా and కుడి ("right") both give
కుడ, so the query కుడి also finds "రాముడు కూడా వచ్చాడు".
- What does not go wrong. Index and query share the chain, so plain OR queries still find their documents. No exception, no corruption, and no unusual cost observed: 20,000 seeded random Telugu texts gave 0 exceptions, and 200,000 copies of కూడా take the same 4,188 index bytes as 200,000 copies of an ordinary word (one codec, one single-document segment). Deterministic; no scale needed.
- How common. Every such index gets these terms whenever its text contains one of the 13 words; their corpus frequency was not measured. Impact class: minor relevance quality. Compared with the intended stop-word behaviour it does lose matches for AND and phrase queries that contain one of the words (above), and the stem collision adds false ones; plain OR queries are not affected.
- Reindex. Any fix changes indexed and query terms, so affected fields need reindexing.
The test shows that the stock new TeluguAnalyzer() does not remove its own default stop word కూడా ('also'), because its StopFilter runs after normalization and the list is not normalized.
/*
* 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.te;
import org.apache.lucene.tests.analysis.BaseTokenStreamTestCase;
public class TestTeluguDefaultStopword extends BaseTokenStreamTestCase {
public void testDefaultStopwordRemoved() throws Exception {
try (TeluguAnalyzer a = new TeluguAnalyzer()) {
assertTrue(a.getStopwordSet().contains("కూడా"));
// expected: no token for the stop word కూడా; actual: the token కుడ (normalized, stemmed)
assertAnalyzesTo(a, "కూడా", new String[0]);
}
}
}
# in a copy of this finding's repro folder (repro/LFV-677/, 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/analysis/common/build/libs/lucene-analysis-common-$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.analysis.te.TestTeluguDefaultStopword
- Expected vs actual
- Expected no token; actual one token:
TokenStream has more tokens than expected (expected count=0); extra token=కుడ(-ea and -da).
More files, environment, full output45 words
| lucene | Lucene main e1faeba08bde (2026-11-02) |
|---|---|
| jdk | 25.0.3+9-LTS |
# Lucene main 645d4fe14a20, assertions on (-ea), seed DEADBEEF: 1 test(s) run, 1 failed
=> java.lang.AssertionError: TokenStream has more tokens than expected (expected count=0); extra token=కుడ
org.junit.Assert.fail(Assert.java:89)
org.apache.lucene.tests.analysis.BaseTokenStreamTestCase.assertTokenStreamContents(BaseTokenStreamTestCase.java:402)
org.apache.lucene.tests.analysis.BaseTokenStreamTestCase.assertTokenStreamContents(BaseTokenStreamTestCase.java:513)
org.apache.lucene.tests.analysis.BaseTokenStreamTestCase.assertTokenStreamContents(BaseTokenStreamTestCase.java:573)
org.apache.lucene.tests.analysis.BaseTokenStreamTestCase.assertTokenStreamContents(BaseTokenStreamTestCase.java:637)
org.apache.lucene.tests.analysis.BaseTokenStreamTestCase.assertAnalyzesTo(BaseTokenStreamTestCase.java:792)
org.apache.lucene.tests.analysis.BaseTokenStreamTestCase.assertAnalyzesTo(BaseTokenStreamTestCase.java:777)
org.apache.lucene.tests.analysis.BaseTokenStreamTestCase.assertAnalyzesTo(BaseTokenStreamTestCase.java:862)
org.apache.lucene.analysis.te.TestTeluguDefaultStopword.testDefaultStopwordRemoved(TestTeluguDefaultStopword.java:27)- Hindi's analyzer has the same chain shape, and its list "also contains forms normalized by HindiNormalizer".
- Arabic filters stop words before normalizing because "the stopword list is not normalized!"; Persian after, because its list "is normalized with the above!"
- No test, review or changelog records a choice; the Telugu tests never check that a stop word is removed.
The full argument365 words
- Contract (implied, not literal).
TeluguAnalyzer()"Builds an analyzer with the default stop words" (:98), the field is the "File containing default Telugu stopwords." (:42-43), and thecreateComponentsjavadoc lists "Telugu Stop words" in the chain. No sentence literally promises that every entry is removed, but an entry no input can reach through the default analyzer (no stem-exclusion set) is dead data in a shipped default resource. - Lucene's other stop files and analyzers state the same rule for their own lists (a convention stated file by file, not a general API contract): a list must match the token form where its
StopFilterruns:hi/stopwords.txt:4-7("by default this file also contains forms normalized by HindiNormalizer for spelling variation ... such that it can be used whether or not you enable that feature. When adding additional entries to this list, please add the normalized form as well.");ArabicAnalyzer.java:135("the order here is important: the stopword list is not normalized!", so itsStopFilterruns before normalization) andar/stopwords.txt:4;PersianAnalyzer.java:136-137andfa/stopwords.txt:4-5("used after normalization");ckb/stopwords.txt:2("normalized with our scheme");el/stopwords.txt:2-3. - A copied template. The Telugu
createComponentsjavadoc isHindiAnalyzer's with Hindi replaced by Telugu andLowerCaseFilterleft out, and the chain has the same shape (HindiAnalyzer.java:122-129; Hindi also lowercases first); the Hindi list rule was not copied. - No recorded choice.
te/stopwords.txthas exactly one commit (the one that added the analyzer). The list, the four folds and the filter order were all in PR #275's first commit; the PR has one review ("LGTM") and no line comments; #11121, CHANGES and MIGRATE say nothing about Telugu stop words.TestTeluguAnalyzernever checks that a stop word is removed. - Precedent. LUCENE-1966 (#3041, 2009) caught the same mismatch for
ArabicAnalyzerin review and settled it by runningStopFilterfirst. Stop-list data fixes have gone under Bug Fixes (GITHUB#14075) and Improvements (GITHUB#12172, GITHUB#16718), so the CHANGES section is the committers' call. - The
createComponentsjavadoc lists the stop words afterTeluguStemFilterwhile the code runsStopFilterbefore it, andSetKeywordMarkerFilterafter both normalizers while the code adds it before them (HindiAnalyzerhas the same two doc nits); the 13 entries are dead in either order.
The devil's-advocate check205 words
The intended-behaviour devil's advocate and the cross-model reviewer's intended lens built the strongest case for "not a bug": no javadoc promises that every entry is removed; StopFilter is exact matching and never promises to normalize a supplied set; the list looks like rough data (the devil's advocate's non-native reading: a word-by-word translation of the a-words of an English stop list, which would explain the duplicate pairs, the two-word entries and content words such as "available"); the fold is lossy, Telugu normalization has been questioned upstream (#14659, about IndicNormalizer's separate వు -> మ rule, not these four folds), and upstream might prefer to change the fold; and with a stem-exclusion set the raw entries do match.
Why it fails: Lucene's other stop files and analyzers state the rule for their own lists, and TeluguAnalyzer follows neither form of it; a rough list explains how the error happened, but nobody chose to ship entries that can never fire in the default analyzer; "change the fold" is a choice of fix, not intent (the fold is documented: "long -> short vowels matras"); the keyword path needs a user-supplied exclusion set naming the stop word, which the default analyzer never has. Every checker's verdict: real; the adjudication's class: minor.
Recommended Add the 13 normalized forms to te/stopwords.txt (the Hindi style)
Add the 13 normalized forms to te/stopwords.txt, as Hindi's list does (16 lines, no code change). The repro, the Telugu tests and all of lucene/analysis/common pass. Indexed terms change, so affected fields need reindexing, and a Telugu speaker should review the added forms.
All options and trade-offs4 options · 853 words
Two fixes were applied in a scratch worktree at a9779ef3c193 and run through gradle (seed DEADBEEF); the stop-filter-first option and the workaround were not run.
Add the 13 normalized forms to te/stopwords.txt (the Hindi style) (validated by the checks) (recommended)
Append a 3-line comment and the 13 forms that the analyzer's own pre-stop filters produce for the 13 entries. The raw spellings stay, so the list still works in a chain that filters stop words before normalizing. Add the repro (or its first two tests) to the Telugu tests as the regression test; it states a broader rule than any javadoc does, so a test focused on the 13 forms is a less brittle choice if a committer prefers.
--- a/lucene/analysis/common/src/resources/org/apache/lucene/analysis/te/stopwords.txt
+++ b/lucene/analysis/common/src/resources/org/apache/lucene/analysis/te/stopwords.txt
@@ -49,3 +49,19 @@
వద్ద
అందుబాటులో
దూరంగా
+# TeluguAnalyzer applies this list after IndicNormalizationFilter and TeluguNormalizationFilter,
+# which shorten long vowel signs. The forms below are the normalized forms of the entries above
+# that those filters change. When adding entries, please add the normalized form as well.
+చెయగలిగింది
+మళ్ళి
+వ్యతిరెకంగా
+అందరు
+మాత్రమె
+ఇప్పటికె
+కుడా
+అయితె
+వెరుగా
+మెచ్చుకొ
+చుట్టు
+అందుబాటులొ
+దురంగాTrade-offs
- Reindex. Indexed and query terms change for text with the 13 words (old documents keep
కుడ, new queries drop the word): needs a CHANGES entry and a MIGRATE note. - Over-stopping: ask a Telugu speaker to review the 13 forms. The fold can merge distinct words, so a normalized form may also be an ordinary word: చుట్టు, the normal form of చుట్టూ ("around"), is also an ordinary verb form. The Hindi list accepts the same cost; a reviewer may drop individual forms.
getDefaultStopSet()grows from 45 to 58 entries; custom chains that reuse it after the two normalizers are fixed too. No API change.- The list stays consistent only by convention; the comment asks future editors to add normalized forms.
What the checks validated
- Stock main
a9779ef3c193(gradle, seed DEADBEEF): the repro fails 3 of 4 with assertions on and off; the four upstream Telugu test classes pass (16 tests). - With the fix: the repro and the four Telugu classes pass (20 tests, 0 failures) with assertions on, and again with assertions off; the repro alone passes with assertions off (4 tests); all of
:lucene:analysis:common:testwith the repro added passes (1,913 tests, 0 failures, 18 skipped);ecjLintTestandcheckGoogleJavaFormatpass with the final test file. - Run order, for the record: the stock gradle runs and the module-wide run used the test before
checkGoogleJavaFormatasked for a different line wrapping; the final file differs only in layout, and the fix run with it was repeated. - Through the classpath runner: the repro passes 4 of 4 with
-eaand-dawith the fixed list first on the classpath; the default set then has 58 entries (45 before). fix.diffandfix-with-test.diff(the fix plus the repro as a new test file) passgit apply --checkon a clean tree.
In TeluguAnalyzer.DefaultSetHolder, add normalize("", entry) of every entry (the same DecimalDigitFilter, IndicNormalizationFilter and TeluguNormalizationFilter the analyzer runs before its StopFilter) to the loaded set and keep it unmodifiable (+10/-1 lines in TeluguAnalyzer.java).
CharArraySet raw = WordlistLoader.getWordSet(/* te/stopwords.txt, as before */);
// The StopFilter runs after IndicNormalizationFilter and TeluguNormalizationFilter, so also
// add every entry in the form those filters produce (as normalize() does).
CharArraySet set = new CharArraySet(raw, false);
try (TeluguAnalyzer normalizer = new TeluguAnalyzer(CharArraySet.EMPTY_SET)) {
for (Object entry : raw) {
set.add(normalizer.normalize("", new String((char[]) entry)).utf8ToString());
}
}
DEFAULT_STOP_SET = CharArraySet.unmodifiableSet(set);Trade-offs
Future list edits stay consistent automatically and the data file is unchanged; but it is code in a static initializer that builds a temporary TeluguAnalyzer, Analyzer.normalize treats each entry as one token (no tokenizer), so it also adds the normalized form of a two-word entry (harmless for the stock chain; 59 entries), and the same reindex and over-stopping review apply.
What the checks validated
- With the change (gradle, seed DEADBEEF, assertions on): the repro and the four Telugu classes pass (20 tests, 0 failures);
ecjLintMainandcheckGoogleJavaFormatpass. - Through the classpath runner (the changed class shadowed onto the main jars): the repro passes 4 of 4 with
-eaand-da; the default set then has 59 entries. - Not run: the whole module with this change.
Move StopFilter above IndicNormalizationFilter in createComponents.
Trade-offs
Not run. The 13 raw spellings would then match, but the analyzer would stop removing variant spellings that the normalizer folds onto an entry today (for example a word written with a long vowel sign where the list has the short one), and users who worked around the bug with a normalized custom stop set would break. Not recommended.
Pass a stop set that also holds the normalized forms: new TeluguAnalyzer(set) with the default set plus analyzer.normalize("", w) of each entry. The independent reproducer's control shows that a set holding కుడా removes కూడా.
Trade-offs
Changes indexed terms, so it also needs reindexing, and it does not help other users of the default list.
An exhaustive check on the real jar: for every default stop word of the analyzers that run their stop filter after these normalizers, is the analyzer's own normalized form still in the stop set? It flagged 14 of the 45 Telugu entries (13 single-word) and none for Hindi or Persian; the finding was then reduced to a public-API JUnit.
The method in full153 words
Method: bounded-exhaustive checking of the stock jar against the analyzer's own documented chain (semi-formal tier), in a hunt unit that checked script normalizers against their documented rules.
- For every entry of the default stop set of each analyzer that runs
StopFilterafter these normalizers, the check ran the entry through exactly the analyzer's pre-stop filters and asked whether the result is still in the set. Telugu: 14 of 45 entries flagged (the 13 single-word entries plus one two-word entry); Bengali 13; Hindi and Persian 0. - It was confirmed end to end with
Analyzer.tokenStream, then reduced to a 2-test public-API JUnit that fails on the stock main jars with assertions on and off. - Checked by an independent reproducer, a reviewer from another model family (OpenAI GPT-5.6, refute and intended lenses, both executed), an intended-behaviour devil's advocate, an adjudication, a history, prior-report and security follow-up, and an audit. Found 2026-10-27 on main
a9779ef3c193.
No prior report found (2026-10-27): 177 logged queries over GitHub issues, PRs and commits, the Lucene and Solr lists, the JIRA archive, downstream trackers and the web, plus the changed files of all 409 open PRs. Another contributor's public note describes the same mismatch for the Bengali analyzer only.
Searches3 searches
Verdict new for Telugu: no report by anyone; the same mismatch pattern is publicly described for the Bengali analyzer only (a separate bug). Every upstream Telugu item was read in full and none mentions stop words. Keyword search cannot prove absence; a report written only in Telugu script could be missed (two Telugu-script queries found nothing), and private reports are not searchable.
| Where | When | How | Found |
|---|---|---|---|
| GitHub issues, PRs and commits (apache/lucene), downstream repos, dev@, java-user@, issues@, commits@, the JIRA archive, the web | 2026-10-27 | the hunt's prior-report tool: 65 queries (24 GitHub, 3 commit, 2 downstream, 24 list, 6 JIRA, 6 web), 136 distinct hits; a defect-class run (38 queries, 437 hits); the finder's run (24 queries) | no report; nearest: #11121 / PR #275 (added the analyzer), #13059 (normalize() wiring), #14659, #14699, #14657 (IndicNormalizer's Telugu vu -> ma rule) |
| apache/lucene and the OpenSearch, Elasticsearch and Solr repos; all 409 open apache/lucene PRs; commits API on main and branch_10x | 2026-10-27 | 26 issue searches (two in Telugu script), 3 code searches, changed-file scan of every open PR | no report; no open PR touches te/stopwords.txt (#14699 touches IndicNormalizer and Telugu tests only) |
| JIRA (LUCENE, SOLR), 7 mailing lists incl. the Solr lists, wiki notes of a search team's analysis work, the web, the Solr Reference Guide | 2026-10-27 | 4 JIRA and 21 list queries, 10 wiki searches and 8 page reads, 6 web queries | same defect class elsewhere only: LUCENE-1966 (#3041, ArabicAnalyzer, 2009, settled by design), LUCENE-1963 (#3038), #12172; another contributor's 2022 wiki note on BengaliAnalyzer's list (not Telugu) |
Born with the analyzer: 544dbbea468 (LUCENE-10083, PR #275, 2021-09-02), first released in 8.10.0. The list has not changed since; the finder's JUnit fails on all 40 releases from 8.10.0 to 10.5.2 and on main.
Versions tested6 rows
Introduced by 544dbbea468 "LUCENE-10083: Analyzer and stemmer for Telugu language" (apache/lucene PR #275 by @vinodrenu, approved and merged by @rmuir on 2021-09-02; issue #11121), backported to branch_8x of the former lucene-solr repository as 114c9dff46c the same day. Not a regression: the list, the filter order and the four vowel-sign folds were already in the PR's first commit c143ef618e (2021-08-13).
te/stopwords.txt has no later commit and is the same blob at every 9.x and 10.x tag, in the 8.10.0 and 8.11.4 jars, on main, on the unreleased branch_10x head and on the head of branch_10_6, from which 10.6.0 was released on 2026-10-29. The later commits to TeluguAnalyzer.java (LUCENE-10335 stop-file loading, #13059 normalize() only) and to TeluguNormalizer.java (visibility, formatting, a comment typo) do not change this. The te classes say @since 9.0.0, but they first shipped in 8.10.0.
| Version or commit | Verdict | Result |
|---|---|---|
main a9779ef3c193 | affected | this doc's JUnit fails 3 of 4 through gradle (assertions on and off) and the classpath runner (-ea, -da); passes with either fix |
| 10.0.0 to 10.5.2 (12 releases) | affected | the finder's JUnit fails 2 of 2, the same 13 words; this doc's JUnit fails 3 of 4 on 10.5.2 |
| 9.0.0 to 9.12.3 (21 releases) | affected | the finder's JUnit fails 2 of 2 (9.0.0 with the old test-framework import); this doc's JUnit fails 3 of 4 on 9.12.3 |
| 8.10.0, 8.10.1, 8.11.0 to 8.11.4 | first release 8.10.0 | the finder's JUnit (old import) fails 2 of 2; 8.10.0 with only the fixed list first on the classpath passes |
544dbbea468 (its te sources over the 9.0.0 jars) | introducing commit | fails 2 of 2, the same message (a consistency check, not a full build of that commit) |
| 8.9.0 | API absent | no TeluguAnalyzer (compile error) |
An independent reproducer, a reviewer from another model family (OpenAI GPT-5.6, two executed lenses), an intended-behaviour devil's advocate, an adjudication and an audit each re-ran it with their own tests or new seeds; all say real, every control passes, and this doc's gradle runs reproduce it on a9779ef3c193.
Checks and verdicts346 words
Every check executed code on the stock main jars; the cross-model reviewer ran on main ea57360912ed, whose lucene/analysis sources equal a9779ef3c193.
| Check | Verdict | What it found |
|---|---|---|
| finder | candidate | Stop-list model over six analyzers (Telugu 14 of 45 flagged, Hindi and Persian 0), then the 2-test JUnit: fails 2 of 2 on main (-ea, -da), 10.5.2 and 9.12.3; passes with the fixed list; upstream Telugu tests 16 of 16 with it. |
| independent reproducer | real | Own 5-test JUnit written from the claim: fails 2 of 5 on main (-ea, -da), 10.5.2, 9.12.3, 8.11.4 and 8.10.0, 3 controls pass; its sweep of 39 analyzers, read entry by entry, leaves single-word defaults lost to pre-stop normalization only in Telugu and Bengali. |
| cross-model review, refute lens (OpenAI GPT-5.6, executed) | real, high | Refutation failed: own pipeline test fails on main (also -da and another seed), 10.5.2 and 9.12.3; the fixed list passes; upstream Telugu tests 16 of 16 with it. |
| cross-model review, intended lens (OpenAI GPT-5.6, executed) | real, high (intended argument moderate) | Strongest case: exact-match StopFilter, lossy fold; rejected because the Arabic, Persian, Hindi and Sorani analyzers align list and filter position. Its survey: those four remove every single-word default. |
| intended-behaviour devil's advocate | real (high on facts, moderate on how upstream files it) | Re-ran the finder's JUnit on main (-ea, -da), 10.5.2, 9.12.3 and 8.10.0; its probe shows the 13 stay dead in the javadoc's order too, the keyword path, and Hindi with 0 dead entries. |
| adjudication | confirmed, high; class minor | Re-ran the independent reproduction's JUnit (2 of 5 fail, -ea), the finder's (2 of 2 fail, -da) and the finder's with the fixed list (2 of 2 pass), seeds 1DEA1234/1DEA1235. |
| audit | keep | Re-ran the finder's JUnit on a clean main checkout with new seeds (-ea, -da): 2 of 2 fail each. |
| this doc's runs | reproduced; fix validated | Gradle in a clean worktree: the repro fails 3 of 4 on stock (assertions on and off) and passes with either fix; lucene/analysis/common passes with the recommended fix. |
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 493 KB
Finding record, Lucene and upstream sources, repro files and diagram notes1,567 words
- Registry title
- TeluguAnalyzer runs its StopFilter after Indic and Telugu normalization, but its default stop list is not normalized, so 13 of the 43 single-word default stop words (for example కూడా, 'also') are never removed
- Area
- analysis-common / language analyzers (TeluguAnalyzer default stop set te/stopwords.txt, applied after IndicNormalizationFilter and TeluguNormalizationFilter)
- Priority
- Low, rank 442: The stock TeluguAnalyzer keeps 13 default stop words, which costs only extra terms, scoring noise and slightly stricter AND and phrase queries, the same minor quality class as its Bengali twin LFV-672, so both go in Low with this new one first, right after LFV-583.
- Impact class
- minor
- Impact note
- Relevance quality and index size only, with no error: every application that analyzes Telugu text with the stock new TeluguAnalyzer() (or reuses TeluguAnalyzer.getDefaultStopSet() after the same two normalization filters), on every release from 8.10.0 through 10.6.0 (10.6.0 by reading) and on main, indexes and queries 13 common function words from the default list (among them కూడా 'also', అయితే, మళ్ళీ, మాత్రమే and ఇప్పటికే) as ordinary terms (కూడా is indexed as కుడ), so they take postings, add scoring noise and leave no position gap, a query made only of them still matches documents, AND and phrase queries that contain one are stricter than with the word removed, and because the stemmer folds a kept word onto unrelated words, a query for కుడి ('right') also matches a document that contains only కూడా (all shown on a 5-document index by the security probe); index and query use the same chain, so plain OR queries still find their documents, no scale is needed, -ea and -da behave the same, and because a fix changes indexed terms, affected fields need reindexing after it.
- Summary
- You analyze Telugu text with the stock new TeluguAnalyzer(), whose default stop list (te/stopwords.txt) holds common function words such as కూడా ('also') that the analyzer should drop. The analyzer normalizes every token before its StopFilter and folds long vowel signs to short ones (ూ to ు, ీ to ి, ే to ె, ో to ొ), but the list holds only the unnormalized spellings, so the StopFilter sees కుడా and never matches the entry కూడా. As a result 13 of the 43 single-word default stop words are never removed and are indexed and searched as ordinary terms, while the Hindi analyzer, which has the same chain, ships the normalized forms in its list and removes all of its stop words.
- Introduced
- 544dbbea46878b49be5c4c4e82d9b06a5679c891 (LUCENE-10083 'Analyzer and stemmer for Telugu language', apache/lucene PR #275 by vinodrenu, approved ('LGTM') and merged by rmuir on 2021-09-02; issue #11121; backported to branch_8x of the former lucene-solr repository as 114c9dff46c the same day) [read: git log --follow in the history clone, PR #275 and its commits through the read-only GitHub API]. It is the commit that added TeluguAnalyzer, so the bug shipped with the feature and is not a regression: the stop list (blob ea175d67), the StopFilter placed after both normalization filters and the four long-vowel-sign folds were already in the PR's first commit c143ef618e (2021-08-13). te/stopwords.txt has had no commit since. The later TeluguAnalyzer.java commits do not change this behaviour: cc342ea7407 (LUCENE-10335, stop-file loading only), 9caeb9395da (#13059, normalize() only), 694d797526a, f99deb58b74 and 54283150a81 (visibility, formatting, comments). (2021-09-02 (merged on main and backported to branch_8x the same day; first released in 8.10.0))
- First affected version
- 8.10.0 [exec] (the first release with TeluguAnalyzer; CHANGES lists LUCENE-10083 under 8.10.0 New Features). 8.9.0, the last release before it, has no Telugu analysis API [exec: the repro does not compile]. On the main line the first affected release is 9.0.0.
- Affected versions
- Every release that has TeluguAnalyzer, 8.10.0 through 10.6.0 (10.6.0 by reading), and main a9779ef3c193; not fixed. [exec] the follow-up's history matrix ran the finder's JUnit (2 tests, public API) 48 times: it fails 2 of 2 with the identical message (same 13 survivors) on all 40 releases (8.10.0, 8.10.1, 8.11.0-8.11.4, the 21 releases 9.0.0-9.12.3 and the 12 releases 10.0.0-10.5.2), on the introducing commit's sources over the 9.0.0 jars and on main, with extra -da runs on 8.10.0, 9.12.3, 10.5.2 and main; 8.9.0 does not compile (API absent); 8.10.0 with only the fixed stop list first on the classpath passes. The independent reproduction's own JUnit fails on main (-ea and -da), 10.5.2, 9.12.3, 8.11.4 and 8.10.0; the intended-behaviour check re-ran the finder's JUnit on main (-ea and -da) and on 10.5.2, 9.12.3 and 8.10.0 (-da), all failing. [read] te/stopwords.txt is the same blob (ea175d67) at every 9.x and 10.x tag, inside the 8.10.0 and 8.11.4 jars, on main, on the unreleased branch_10x head and on the head of branch_10_6, from which 10.6.0 was released on 2026-10-29 (read through the GitHub contents API on 2026-10-27, not executed); upstream main a95c754ee003 (2026-10-26) is unchanged.
- Status
- documented (brief, checks and a per-finding doc exist)
- Verification
- Confirmed by AI-run checks: an independent repro, a cross-model review that agreed, an intended-behaviour check, a prior-report search and a skeptical audit; not yet triaged by a person.
- Found by
- semi-formal: exhaustive check
- Upstream and prior reports
- none found (searched 2026-10-27, read-only; the prior-report record, verdict new): 177 logged queries, among them a scripted prior-report search (65 queries: 24 GitHub, 3 commit, 2 downstream, 24 mailing-list queries over the dev, java-user, issues and commits lists, 6 JIRA, 6 web; 136 distinct hits, none rated high), a defect-class run (38 queries, 437 hits), the finder's earlier run (24 queries) and 74 manual searches (26 GitHub issue searches including two in Telugu script, 3 code searches, a scan of the touched files of all 409 open apache/lucene PRs, commits-API checks on main and branch_10x, 4 JIRA queries over LUCENE and SOLR, 21 queries on 7 lists including the Solr lists, mediawiki.org notes pages, 6 web queries, the Solr Reference Guide). Every upstream Telugu item was read in full and none mentions stop words: #11121 / LUCENE-10083 and PR #275 (introduced the analyzer and the list; one 'LGTM' review), #13059 (normalize() wiring fix, a different defect), #14659, #14699 and #14657 (IndicNormalizer's Telugu vu -> ma rule). Same defect class elsewhere, not this bug: LUCENE-1966 (#3041, 2009, ArabicAnalyzer, settled by running StopFilter before normalization), LUCENE-1963 (#3038) and #12172 (other stop-list fixes), and another contributor's public 2022 wiki note that describes the same mismatch for BengaliAnalyzer only. No open PR touches te/stopwords.txt (#14699 touches IndicNormalizer and Telugu tests; #16911 deletes all of branch_10x). Not fixed: upstream main a95c754ee003 and branch_10x unchanged. Gaps: keyword search only (a report written only in Telugu script could be missed; two Telugu-script queries found nothing); private reports and chat are not searchable.
- Lucene commit tested
- main
a9779ef3c193(commit of 2026-10-26): this doc's JUnit through gradle in a clean worktree, stock and with the recommended fix with assertions on and off, and with the load-time fix with assertions on, plus the four upstream Telugu test classes and all oflucene/analysis/commonwith the recommended fix; through the hunt's classpath runner, the same JUnit on main (-ea,-da, both fixes), 10.5.2 and 9.12.3. Hunt runs: the finder's JUnit on all 40 releases from 8.10.0 to 10.5.2, on the introducing commit's sources and on main.
- Finding record
lucene/analysis/common/src/java/org/apache/lucene/analysis/te/TeluguAnalyzer.java:42-43 (at a9779ef3c193) (DEFAULT_STOPWORD_FILE, 'File containing default Telugu stopwords'), :47-54 (getDefaultStopSet), :98-101 (TeluguAnalyzer(): 'Builds an analyzer with the default stop words'), :103-112 (createComponents javadoc, which lists 'Telugu Stop words' after TeluguStemFilter), :114-125 (chain: StandardTokenizer, DecimalDigitFilter, SetKeywordMarkerFilter only with a stem-exclusion set, IndicNormalizationFilter, TeluguNormalizationFilter, StopFilter at :122, TeluguStemFilter), :127-133 (normalize(), no StopFilter)lucene/analysis/common/src/java/org/apache/lucene/analysis/te/TeluguNormalizer.java:76-88 (at a9779ef3c193) ('// long -> short vowels matras': U+0C40 -> U+0C3F, U+0C42 -> U+0C41, U+0C47 -> U+0C46, U+0C4B -> U+0C4A)lucene/analysis/common/src/resources/org/apache/lucene/analysis/te/stopwords.txt:1-51 (at a9779ef3c193) (header only '# Telugu stop word list'; 50 entry lines, 45 distinct, 5 duplicates, multi-word entries at :31 and :45; the 13 dead entries at :2, :11, :12, :14, :18, :20, :21, :22, :37, :39, :43, :50, :51)lucene/analysis/common/src/resources/org/apache/lucene/analysis/hi/stopwords.txt:4-7 (at a9779ef3c193) ('by default this file also contains forms normalized by HindiNormalizer ... please add the normalized form as well'); ckb/stopwords.txt:2 ('normalized with our scheme'); lucene/analysis/common/src/java/org/apache/lucene/analysis/ar/ArabicAnalyzer.java:135-136 ('the order here is important: the stopword list is not normalized!', StopFilter before normalization); fa/PersianAnalyzer.java:136 ('the stopword list is normalized with the ...')lucene/analysis/common/src/test/org/apache/lucene/analysis/te/TestTeluguAnalyzer.java (at a9779ef3c193) (no test checks that a default stop word is removed); lucene/CHANGES.txt:5215 ('LUCENE-10083: Analyzer and stemmer for Telugu language' under 8.10.0, section at :5192) and :3048 ('GITHUB#13059: Fixed missing IndicNormalization and DecimalDigit filters in TeluguAnalyzer normalization' under 9.10.0)GitHub, read-only (2026-10-27): apache/lucene PR #275 (commits c143ef618e, 6c0c09933f; review), #11121, #13059, #14659, #14699, #14657, #3041 (LUCENE-1966), #3038 (LUCENE-1963), #12172, #16911; upstream main a95c754ee003 and branch_10x te/stopwords.txt unchanged
- The finding in 30 seconds
lucene/analysis/common/src/java/org/apache/lucene/analysis/te/TeluguAnalyzer.java:42-43, :94-101, :114-125, :127-133lucene/analysis/common/src/java/org/apache/lucene/analysis/te/TeluguNormalizer.java:76-88lucene/core/src/java/org/apache/lucene/analysis/StopFilter.java:96-97lucene/analysis/common/src/resources/org/apache/lucene/analysis/te/stopwords.txt:1-51 (lines 2, 11, 12, 14, 18, 20, 21, 22, 37, 39, 43, 50, 51; :31, :45 two-word)
- Why it looks unintended
lucene/analysis/common/src/java/org/apache/lucene/analysis/te/TeluguAnalyzer.java:42-43, :98, :103-112lucene/analysis/common/src/resources/org/apache/lucene/analysis/hi/stopwords.txt:4-7; lucene/analysis/common/src/resources/org/apache/lucene/analysis/ar/stopwords.txt:4-6; lucene/analysis/common/src/resources/org/apache/lucene/analysis/fa/stopwords.txt:4-5; lucene/analysis/common/src/resources/org/apache/lucene/analysis/ckb/stopwords.txt:2; lucene/analysis/common/src/resources/org/apache/lucene/analysis/el/stopwords.txt:2-3lucene/analysis/common/src/java/org/apache/lucene/analysis/ar/ArabicAnalyzer.java:135-138; lucene/analysis/common/src/java/org/apache/lucene/analysis/fa/PersianAnalyzer.java:135-139; lucene/analysis/common/src/java/org/apache/lucene/analysis/hi/HindiAnalyzer.java:110-119, :123-129lucene/analysis/common/src/test/org/apache/lucene/analysis/te/TestTeluguAnalyzer.java (six tests, none checks stop-word removal)
- History
lucene/CHANGES.txt:5192, :5215 (LUCENE-10083 under 8.10.0), :3048 (GITHUB#13059, 9.10.0)
- TestTeluguDefaultStopword.java (JUnit test):
repro/LFV-677/src/org/apache/lucene/analysis/te/TestTeluguDefaultStopword.java(30 lines)
A summary card (the PNG), made in parallel with this doc; the alt text describes the PNG as delivered on 2026-10-28.
assets/LFV-677/bug.png(493 KB)
