{"record":{"id":"a46b81f5c2eb61f4","repo":"languagetool-org/languagetool","slug":"ip-request-size-limit-of-bytes-per-seconds-excee-a46b81","errorCode":null,"errorMessage":"IP request size limit of  bytes per  seconds exceeded","messagePattern":"IP request size limit of  bytes per  seconds exceeded","errorType":"http","errorClass":"TooManyRequestsException","httpStatus":429,"severity":"warning","filePath":"languagetool-server/src/main/java/org/languagetool/server/RequestLimiter.java","lineNumber":245,"sourceCode":"          if (ipFingerprintFactor > 0 && requestLimitInBytes > 0 && requestSizeByFingerprint > requestLimitInBytes) {\n            String msg = \"limit in Mode.TEXTLEVEL_ONLY: \" + requestLimitInBytes + \" / \" + requestLimitPeriodInSeconds + \", request size: \"  + requestSizeByIp + \", ip: \" + ipAddress + \", fingerprint: \" + fingerprint;\n            throw new TooManyRequestsException(\"Client request size limit of \" + requestLimitInBytes + \" bytes per \" +\n              requestLimitPeriodInSeconds + \" seconds exceeded in text-level checks\");\n          }\n          if (requestLimitInBytes > 0 && requestSizeByIp > ipRequestLimitInBytes) {\n            String msg = \"limit in Mode.TEXTLEVEL_ONLY: \" + ipRequestLimitInBytes + \" / \" + requestLimitPeriodInSeconds + \", request size: \"  + requestSizeByIp + \", ip: \" + ipAddress + \", fingerprint: \" + fingerprint;\n            throw new TooManyRequestsException(\"IP request size limit of \" + ipRequestLimitInBytes + \" bytes per \" +\n              requestLimitPeriodInSeconds + \" seconds exceeded in text-level checks\");\n          }\n        } else {\n          if (ipFingerprintFactor > 0 && requestLimitInBytes > 0 && requestSizeByFingerprint > requestLimitInBytes) {\n            String msg = \"limit: \" + requestLimitInBytes + \" / \" + requestLimitPeriodInSeconds + \", request size: \"  + requestSizeByIp + \", ip: \" + ipAddress + \", fingerprint: \" + fingerprint;\n            throw new TooManyRequestsException(\"Client request size limit of \" + requestLimitInBytes + \" bytes per \" +\n              requestLimitPeriodInSeconds + \" seconds exceeded\");\n          }\n          if (requestLimitInBytes > 0 && requestSizeByIp > ipRequestLimitInBytes) {\n            String msg = \"limit: \" + ipRequestLimitInBytes + \" / \" + requestLimitPeriodInSeconds + \", request size: \"  + requestSizeByIp + \", ip: \" + ipAddress + \", fingerprint: \" + fingerprint;\n            throw new TooManyRequestsException(\"IP request size limit of \" + ipRequestLimitInBytes + \" bytes per \" +\n              requestLimitPeriodInSeconds + \" seconds exceeded\");\n          }\n        }\n      }\n    }\n  }\n  \n  protected static class RequestEvent {\n\n    private final String ip;\n    private final Date date;\n    private final int sizeInBytes;\n    private final String fingerprint;\n    private final JLanguageTool.Mode mode;\n\n    RequestEvent(String ip, Date date, int sizeInBytes, String fingerprint, JLanguageTool.Mode mode) {\n      this.ip = ip;\n      this.date = new Date(date.getTime());","sourceCodeStart":227,"sourceCodeEnd":263,"githubUrl":"https://github.com/languagetool-org/languagetool/blob/2e990059ce67d5e2a0f7f7ca5d31160c6709df4b/languagetool-server/src/main/java/org/languagetool/server/RequestLimiter.java#L227-L263","documentation":"Outside TEXTLEVEL_ONLY mode, RequestLimiter enforces a per-IP request-size quota. When total bytes from one IP exceed ipRequestLimitInBytes within requestLimitPeriodInSeconds, checkLimit throws TooManyRequestsException (HTTP 429). This caps aggregate upload volume per client IP for all check modes.","triggerScenarios":"POSTing to /v2/check (any non-textLevelOnly mode) where cumulative bytes from the client IP exceed ipRequestLimitInBytes within requestLimitPeriodInSeconds and requestLimitInBytes > 0.","commonSituations":"Server-side integrations routing many users through one outbound IP; scheduled jobs re-uploading the same corpora; retries without backoff after a 429; tight ipRequestLimitInBytes in self-hosted config.","solutions":["Pause until requestLimitPeriodInSeconds elapses, then resume slowly","Implement exponential backoff on 429 and cap total bytes sent per IP per window","Use Premium credentials for higher limits, or distribute calls across IPs","Server admins: raise ipRequestLimitInBytes in HTTPServerConfig / launch arguments","Batch and deduplicate texts before sending to reduce total bytes"],"exampleFix":"// before: immediate retry on 429\ncatch (e) { check(text); }\n// after\ncatch (TooManyRequests/429 e) { sleep(backoff); backoff *= 2; retry(); }","handlingStrategy":"retry","validationCode":"function withinIpBudget(bytes) { return ipBytesWindow + bytes <= ipRequestLimitInBytes; }","typeGuard":null,"tryCatchPattern":"catch (err) {\n  if (err instanceof HttpError && err.status === 429) {\n    await sleep(backoffMs); backoffMs = Math.min(backoffMs * 2, MAX_BACKOFF);\n    return attempt();\n  }\n  throw err;\n}","preventionTips":["Implement exponential backoff with jitter for 429 responses","Deduplicate and batch texts to cut total uploaded bytes","Route through authenticated accounts or multiple egress IPs if legitimately high-volume","Tune ipRequestLimitInBytes on self-hosted deployments to match real usage"],"tags":["rate-limit","http","http-429","languagetool"],"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-14T00:17:10.932Z"}