languagetool-org/languagetool · error · RuntimeException
Expected at least '1grams', '2grams', and '3grams' sub direc
Error message
Expected at least '1grams', '2grams', and '3grams' sub directories but only got ${dirs} in ${topIndexDir.getAbsolutePath()} What it means
LuceneSingleIndexLanguageModel requires all three of '1grams', '2grams', '3grams' subdirectories. If validateDirectory finds some but fewer than three, it throws RuntimeException listing which ones were found and where.
Source
Thrown at languagetool-core/src/main/java/org/languagetool/languagemodel/LuceneSingleIndexLanguageModel.java:72
*/
public static void validateDirectory(File topIndexDir) {
if (!topIndexDir.exists() || !topIndexDir.isDirectory()) {
throw new RuntimeException("Not found or is not a directory:\n" +
topIndexDir + "\n" +
"As ngram directory, please select the directory that has a subdirectory like 'en'\n" +
"(or whatever language code you're using).");
}
List<String> dirs = new ArrayList<>();
for (String name : topIndexDir.list()) {
if (name.matches("[123]grams")) {
dirs.add(name);
}
}
if (dirs.isEmpty()) {
throw new RuntimeException("Directory must contain at least '1grams', '2grams', and '3grams': " + topIndexDir.getAbsolutePath());
}
if (dirs.size() < 3) {
throw new RuntimeException("Expected at least '1grams', '2grams', and '3grams' sub directories but only got " + dirs + " in " + topIndexDir.getAbsolutePath());
}
}
/**
* Only used internally.
* @since 3.2
*/
@Experimental
public static void clearCaches() {
dirToSearcherMap.clear();
}
/**
* @param topIndexDir a directory which contains at least another sub directory called {@code 3grams},
* which is a Lucene index with ngram occurrences as created by
* {@code org.languagetool.dev.FrequencyIndexCreator}.
*/
public LuceneSingleIndexLanguageModel(File topIndexDir) {View on GitHub (pinned to 2e990059ce)
Solutions
- Re-download/re-extract the full ngram index so 1grams, 2grams and 3grams all exist
- Move/copy the missing ngrams subdirectory into the index dir
- Use a constructor/variant that matches the available data
Example fix
// before ls /data/en/ -> 1grams 2grams // 3grams missing // after unzip en-ngrams.zip -d /data/en/ # restores 1grams 2grams 3grams
Defensive patterns
Strategy: validation
Validate before calling
Set<String> found = new HashSet<>();
for (File f : ngramDir.listFiles(File::isDirectory)) if (f.getName().matches("[123]grams")) found.add(f.getName());
if (found.size() < 3) throw new IllegalStateException("Missing ngram dirs, only got: " + found); Try / catch
try { model = new LuceneSingleIndexLanguageModel(ngramDir); } catch (RuntimeException e) { if (e.getMessage().startsWith("Expected at least")) { reDownloadNgramIndex(ngramDir); model = new LuceneSingleIndexLanguageModel(ngramDir); } else throw e; } Prevention
- Checksum-verify ngram archives after download to catch partial extractions
- Never manually delete individual ngrams subdirectories to reclaim space
- Compare the extracted directory listing against the archive before deploying
When it happens
Trigger: Constructing the model against an index directory containing only a subset of the required ngram subdirectories (e.g. only 1grams and 2grams present).
Common situations: Partial or corrupted download/extraction of the ngram index; manually deleting a large ngram folder to save space; a different maxNgram index layout passed to the wrong constructor.
Related errors
- Directory must contain at least '1grams', '2grams', and '3gr
- No directories '1grams' ... '3grams' found in ${topIndexDir}
- Requested ${tokens.size()}gram but index has only up to ${ma
- Expected 'totalTokenCount' meta documents not found in 1gram
- grams must be between 1 and 5:
AI-assisted analysis of languagetool-org/languagetool@2e990059ce (2026-09-06).
Data as JSON: /api/errors/b014d9ca5e39ae71.
Report an issue: GitHub.