{"record":{"id":"9b8a31810f1e3755","repo":"languagetool-org/languagetool","slug":"search-timeout-of-maxsearchtimemillis-ms-reach","errorCode":null,"errorMessage":"Search timeout of + maxSearchTimeMillis + ms reached for query + query","messagePattern":"Search timeout of \\+ maxSearchTimeMillis \\+ ms reached for query \\+ query","errorType":"exception","errorClass":"SearchTimeoutException","httpStatus":null,"severity":"error","filePath":"languagetool-wikipedia/src/main/java/org/languagetool/dev/index/Searcher.java","lineNumber":183,"sourceCode":"      SearchRunnable runnable = new SearchRunnable(indexSearcher, query, languageTool);\n      Thread searchThread = new Thread(runnable);\n      searchThread.start();\n      try {\n        // using a TimeLimitingCollector is not enough, as it doesn't cover all time required to\n        // search for a complicated regex, so interrupt the whole thread instead:\n        if (limitSearch) { // I don't know a simpler way to achieve this...\n          searchThread.join(maxSearchTimeMillis);\n        } else {\n          searchThread.join(Integer.MAX_VALUE);\n        }\n      } catch (InterruptedException e) {\n        throw new RuntimeException(\"Search thread got interrupted for query \" + query, e);\n      }\n      if (searchThread.isAlive()) {\n        // join() timed out and the thread is still running, so its result isn't ready yet -\n        // interrupt it and bail out instead of falling through with a null result\n        searchThread.interrupt();\n        throw new SearchTimeoutException(\"Search timeout of \" + maxSearchTimeMillis + \"ms reached for query \" + query);\n      }\n      Exception exception = runnable.getException();\n      if (exception != null) {\n        if (exception instanceof SearchTimeoutException) {\n          throw (SearchTimeoutException)exception;\n        }\n        throw new RuntimeException(\"Exception during search for query \" + query + \" on rule \" + rule.getId(), exception);\n      }\n\n      List<MatchingSentence> matchingSentences = runnable.getMatchingSentences();\n      SearcherResult searcherResult = new SearcherResult(matchingSentences, runnable.docsChecked, query);\n      searcherResult.setMaxDocChecked(runnable.getMaxDocChecked());\n      searcherResult.setHasTooManyLuceneMatches(runnable.hasTooManyLuceneMatches());\n      searcherResult.setLuceneMatchCount(runnable.getLuceneMatchCount());\n      searcherResult.setSkipHits(skipHits);\n      searcherResult.setNumDocs(runnable.numDocs);\n      if (runnable.hasTooManyLuceneMatches()) {\n        // more potential matches than we can check in an acceptable time :-(","sourceCodeStart":165,"sourceCodeEnd":201,"githubUrl":"https://github.com/languagetool-org/languagetool/blob/2e990059ce67d5e2a0f7f7ca5d31160c6709df4b/languagetool-wikipedia/src/main/java/org/languagetool/dev/index/Searcher.java#L165-L201","documentation":"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.","triggerScenarios":"Calling findRuleMatchesOnIndex with a query whose Lucene search takes longer than maxSearchTimeMillis (large index, overly broad query, slow disk, missing index optimization).","commonSituations":"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.","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"],"exampleFix":"// before\nSearcherResult result = searcher.findRuleMatchesOnIndex(query, rule, maxSearchTimeMillis = 5000);\n// after\nSearcherResult result = searcher.findRuleMatchesOnIndex(query, rule, 60000); // raise timeout","handlingStrategy":"try-catch","validationCode":"if (maxSearchTimeMillis <= 0 || query == null) throw new IllegalArgumentException(\"query required, positive timeout\");","typeGuard":null,"tryCatchPattern":"try {\n  result = searcher.findRuleMatchesOnIndex(query, rule, timeout);\n} catch (SearchTimeoutException e) {\n  LOG.warn(\"timeout for \" + query + \"; retrying with \" + timeout*4);\n  result = searcher.findRuleMatchesOnIndex(query, rule, timeout * 4);\n}","preventionTips":["Budget timeouts relative to index size","Test broad rules on a small corpus first","Keep the Lucene index optimized"],"tags":["lucene","timeout","search"],"backgroundTag":"request-timeout","analyzedSha":"2e990059ce67d5e2a0f7f7ca5d31160c6709df4b","analyzedAt":"2026-09-06T09:20:17.015Z","contentChangedAt":"2026-09-06T09:20:17.015Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}