languagetool-org/languagetool · error · SearchTimeoutException
Search timeout of + maxSearchTimeMillis + ms reached for…
Error message
Search timeout of + maxSearchTimeMillis + ms reached for query + query
What it means
Searcher.findRuleMatchesOnIndex runs the Lucene search on a worker thread and joins it with a timeout. If the thread is still alive after maxSearchTimeMillis, the thread is interrupted and this SearchTimeoutException is thrown. It means the index query could not finish within the configured time budget.
Solutions
- Increase the maxSearchTimeMillis timeout value passed to the search
- Narrow the query or the pattern rule so fewer documents are scanned
- Optimize/compact the Lucene index and ensure it is on fast local storage
- Run the search on a smaller corpus subset first to validate the rule
Example fix
// before SearcherResult result = searcher.findRuleMatchesOnIndex(query, rule, maxSearchTimeMillis = 5000); // after SearcherResult result = searcher.findRuleMatchesOnIndex(query, rule, 60000); // raise timeout
Defensive patterns
Strategy: try-catch
Validate before calling
if (maxSearchTimeMillis <= 0 || query == null) throw new IllegalArgumentException("query required, positive timeout"); Try / catch
try {
result = searcher.findRuleMatchesOnIndex(query, rule, timeout);
} catch (SearchTimeoutException e) {
LOG.warn("timeout for " + query + "; retrying with " + timeout*4);
result = searcher.findRuleMatchesOnIndex(query, rule, timeout * 4);
} Prevention
- Budget timeouts relative to index size
- Test broad rules on a small corpus first
- Keep the Lucene index optimized
When it happens
Trigger: Calling findRuleMatchesOnIndex with a query whose Lucene search takes longer than maxSearchTimeMillis (large index, overly broad query, slow disk, missing index optimization).
Common situations: Running the Wikipedia corpus tooling against a large or poorly optimized Lucene index; queries with too-broad regex/pattern rules scanning huge numbers of docs; slow I/O on shared hardware.
Understand the failure class
Background: Request timed out: what client-side request timeouts mean across libraries (Request timed out, TIMED_OUT, APITimeoutError) — this error's family across 39 libraries.
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- Exception during search for query + query + on rule +…
- No hits for
- Did not expect more than 1000 'totalTokenCount' meta…
- Directory must contain at least '1grams', '2grams', and…
- ${e.getMessage()}
AI-assisted analysis of languagetool-org/languagetool@2e990059ce (2026-09-06).
Data as JSON: /api/errors/9b8a31810f1e3755.
Report an issue: GitHub.
Appendix: source
Thrown at languagetool-wikipedia/src/main/java/org/languagetool/dev/index/Searcher.java:183
SearchRunnable runnable = new SearchRunnable(indexSearcher, query, languageTool);
Thread searchThread = new Thread(runnable);
searchThread.start();
try {
// using a TimeLimitingCollector is not enough, as it doesn't cover all time required to
// search for a complicated regex, so interrupt the whole thread instead:
if (limitSearch) { // I don't know a simpler way to achieve this...
searchThread.join(maxSearchTimeMillis);
} else {
searchThread.join(Integer.MAX_VALUE);
}
} catch (InterruptedException e) {
throw new RuntimeException("Search thread got interrupted for query " + query, e);
}
if (searchThread.isAlive()) {
// join() timed out and the thread is still running, so its result isn't ready yet -
// interrupt it and bail out instead of falling through with a null result
searchThread.interrupt();
throw new SearchTimeoutException("Search timeout of " + maxSearchTimeMillis + "ms reached for query " + query);
}
Exception exception = runnable.getException();
if (exception != null) {
if (exception instanceof SearchTimeoutException) {
throw (SearchTimeoutException)exception;
}
throw new RuntimeException("Exception during search for query " + query + " on rule " + rule.getId(), exception);
}
List<MatchingSentence> matchingSentences = runnable.getMatchingSentences();
SearcherResult searcherResult = new SearcherResult(matchingSentences, runnable.docsChecked, query);
searcherResult.setMaxDocChecked(runnable.getMaxDocChecked());
searcherResult.setHasTooManyLuceneMatches(runnable.hasTooManyLuceneMatches());
searcherResult.setLuceneMatchCount(runnable.getLuceneMatchCount());
searcherResult.setSkipHits(skipHits);
searcherResult.setNumDocs(runnable.numDocs);
if (runnable.hasTooManyLuceneMatches()) {
// more potential matches than we can check in an acceptable time :-(View on GitHub (pinned to 2e990059ce)