{"record":{"id":"c68acf1706685db0","repo":"languagetool-org/languagetool","slug":"errorratetoohigh-is-reached-by-a-single-sentence-a","errorCode":null,"errorMessage":"ErrorRateTooHigh is reached by a single sentence after rule: <rule.getFullId()>. The whole text contains <wordCounter> words and this sentence has <sentenceMatches.size()> matches.","messagePattern":"ErrorRateTooHigh is reached by a single sentence after rule: <rule\\.getFullId\\(\\)>\\. The whole text contains <wordCounter> words and this sentence has <sentenceMatches\\.size\\(\\)> matches\\.","errorType":"exception","errorClass":"ErrorRateTooHighException","httpStatus":null,"severity":"warning","filePath":"languagetool-core/src/main/java/org/languagetool/JLanguageTool.java","lineNumber":1617,"sourceCode":"      }\n      if (checkCancelledCallback != null && checkCancelledCallback.checkCancelled()) {\n        break;\n      }\n      RuleMatch[] thisMatches = rule.match(analyzedSentence);\n      Collections.addAll(sentenceMatches, thisMatches);\n      if (wordCounter > 0) {\n        //check if the maxErrorsPerWordRate is already reached for the full text with this sentence and rule  \n        float errorsPerWord = sentenceMatches.size() / (float) wordCounter;\n        if (tmpErrorsPerWord < errorsPerWord) {\n          errorRateLog.add(\"With rule: \" + rule.getFullId() + \" \" + (i+1) + \"/\" + rulesSize + \" the sentence error rate increased by: \" + (errorsPerWord - tmpErrorsPerWord) + \" from: \" + tmpErrorsPerWord + \" to total: \" + errorsPerWord);\n          tmpErrorsPerWord = errorsPerWord;\n        }\n        if (maxErrorsPerWordRate > 0 && errorsPerWord > maxErrorsPerWordRate && wordCounter > 25) {\n          errorRateLog.forEach(e -> logger.info(LoggingTools.BAD_REQUEST, e));\n          logger.info(LoggingTools.BAD_REQUEST, \"ErrorRateTooHigh is reached by a single sentence after rule: \" + rule.getFullId() + \". \" +\n            \"The whole text contains \" + wordCounter + \" words \" +\n            \" and this sentence has \" + sentenceMatches.size() + \" matches.\");\n          throw new ErrorRateTooHighException(\"ErrorRateTooHigh is reached by a single sentence after rule: \" + rule.getFullId() + \". \" +\n            \"The whole text contains \" + wordCounter + \" words \" +\n            \"and this sentence has \" + sentenceMatches.size() + \" matches.\");\n        }\n      }\n    }\n    if (sentenceMatches.isEmpty()) {\n      return sentenceMatches;\n    }\n\n    AnnotatedText text = new AnnotatedTextBuilder().addText(analyzedSentence.getText()).build();\n    // rules can create matches with rule IDs different from the original rule (see e.g. RemoteRules)\n    // so while we can't avoid execution of these rules, we still want disabling them to work\n    // so do another pass with ignoreRule here\n    sentenceMatches = sentenceMatches.stream()\n      .filter(match -> !ignoreRule(match.getRule())).collect(Collectors.toList());\n    return applyCustomFilters(new SameRuleGroupFilter().filter(sentenceMatches), text);\n  }\n","sourceCodeStart":1599,"sourceCodeEnd":1635,"githubUrl":"https://github.com/languagetool-org/languagetool/blob/2e990059ce67d5e2a0f7f7ca5d31160c6709df4b/languagetool-core/src/main/java/org/languagetool/JLanguageTool.java#L1599-L1635","documentation":"During check(), if the number of matches in a single sentence exceeds the configured maxErrorsPerWordRate (and the text has more than 25 words), LanguageTool logs the offending rule and throws ErrorRateTooHighException. This protects servers from pathological texts that would produce enormous, useless result sets.","triggerScenarios":"Checking a text where one sentence produces a match density above maxErrorsPerWordRate after some rule (rule.getFullId() names it), with total text length > 25 words. Configured via the error-rate/maximum-error settings of JLanguageTool.","commonSituations":"Posting garbage/random character strings or encrypted/minified text to a LanguageTool server; a overly broad custom or regex rule matching far too often; non-natural-language input (code, base64) submitted for proofreading.","solutions":["Inspect rule.getFullId() in the message — a specific rule is firing excessively; disable or tighten it if it is a custom rule.","Check the submitted text: if it is garbage/non-prose input, validate or sanitize it before checking.","Raise maxErrorsPerWordRate if your legitimate use case produces dense errors.","Catch ErrorRateTooHighException in server code and return a clean 4xx-style response to the client."],"exampleFix":"// before\nList<RuleMatch> matches = lt.check(userText); // throws on garbage input\n// after\ntry {\n  List<RuleMatch> matches = lt.check(userText);\n} catch (ErrorRateTooHighException e) {\n  return badRequest(\"Text rejected: error rate too high (\" + e.getMessage() + \")\");\n}","handlingStrategy":"try-catch","validationCode":"// reject obviously non-prose input before checking\nif (text.chars().filter(Character::isLetter).count() * 100 / text.length() < 40) {\n  throw new IllegalArgumentException(\"input does not look like natural-language text\");\n}","typeGuard":null,"tryCatchPattern":"try {\n  return lt.check(text);\n} catch (ErrorRateTooHighException e) {\n  log.warn(\"Rejected text: {}\", e.getMessage());\n  throw new WebApplicationException(422);\n}","preventionTips":["Sanitize/validate input is natural prose before calling check().","Catch ErrorRateTooHighException explicitly in server handlers.","Review custom rules whose fullId appears in these exceptions.","Tune maxErrorsPerWordRate to your domain's expected error density."],"tags":["error-rate","rule-matches","input-validation","server-protection"],"backgroundTag":"rate-limit-exceeded","analyzedSha":"2e990059ce67d5e2a0f7f7ca5d31160c6709df4b","analyzedAt":"2026-09-06T09:20:17.015Z","contentChangedAt":"2026-09-06T09:20:17.015Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}