languagetool-org/languagetool · warning · TooManyRequestsException

IP request size limit of bytes per seconds exceeded in tex

Error message

IP request size limit of  bytes per  seconds exceeded in text-level checks

What it means

RequestLimiter enforces a per-IP request-size quota in Mode.TEXTLEVEL_ONLY. When the total bytes of requests from one IP address exceed ipRequestLimitInBytes within requestLimitPeriodInSeconds, checkLimit throws TooManyRequestsException (HTTP 429). This limits aggregate bandwidth per client IP regardless of fingerprint.

Source

Thrown at languagetool-server/src/main/java/org/languagetool/server/RequestLimiter.java:234

        }
        if (ipFingerprintFactor > 0 && requestLimit > 0 && requestsByFingerprint > requestLimit) {
          String msg = "limit: " + requestLimit + " / " + requestLimitPeriodInSeconds + ", requests: "  + requestsByIp + ", ip: " + ipAddress + ", fingerprint: " + fingerprint;
          throw new TooManyRequestsException("Client request limit of " + requestLimit + " requests per " +
            requestLimitPeriodInSeconds + " seconds exceeded"); }
        if (requestLimit > 0 && requestsByIp > ipRequestLimit) {
          String msg = "limit: " + ipRequestLimit + " / " + requestLimitPeriodInSeconds + ", requests: "  + requestsByIp + ", ip: " + ipAddress + ", fingerprint: " + fingerprint;
          throw new TooManyRequestsException("IP request limit of " + ipRequestLimit + " requests per " +
            requestLimitPeriodInSeconds + " seconds exceeded");
        }
        if (event.mode == JLanguageTool.Mode.TEXTLEVEL_ONLY) {
          if (ipFingerprintFactor > 0 && requestLimitInBytes > 0 && requestSizeByFingerprint > requestLimitInBytes) {
            String msg = "limit in Mode.TEXTLEVEL_ONLY: " + requestLimitInBytes + " / " + requestLimitPeriodInSeconds + ", request size: "  + requestSizeByIp + ", ip: " + ipAddress + ", fingerprint: " + fingerprint;
            throw new TooManyRequestsException("Client request size limit of " + requestLimitInBytes + " bytes per " +
              requestLimitPeriodInSeconds + " seconds exceeded in text-level checks");
          }
          if (requestLimitInBytes > 0 && requestSizeByIp > ipRequestLimitInBytes) {
            String msg = "limit in Mode.TEXTLEVEL_ONLY: " + ipRequestLimitInBytes + " / " + requestLimitPeriodInSeconds + ", request size: "  + requestSizeByIp + ", ip: " + ipAddress + ", fingerprint: " + fingerprint;
            throw new TooManyRequestsException("IP request size limit of " + ipRequestLimitInBytes + " bytes per " +
              requestLimitPeriodInSeconds + " seconds exceeded in text-level checks");
          }
        } else {
          if (ipFingerprintFactor > 0 && requestLimitInBytes > 0 && requestSizeByFingerprint > requestLimitInBytes) {
            String msg = "limit: " + requestLimitInBytes + " / " + requestLimitPeriodInSeconds + ", request size: "  + requestSizeByIp + ", ip: " + ipAddress + ", fingerprint: " + fingerprint;
            throw new TooManyRequestsException("Client request size limit of " + requestLimitInBytes + " bytes per " +
              requestLimitPeriodInSeconds + " seconds exceeded");
          }
          if (requestLimitInBytes > 0 && requestSizeByIp > ipRequestLimitInBytes) {
            String msg = "limit: " + ipRequestLimitInBytes + " / " + requestLimitPeriodInSeconds + ", request size: "  + requestSizeByIp + ", ip: " + ipAddress + ", fingerprint: " + fingerprint;
            throw new TooManyRequestsException("IP request size limit of " + ipRequestLimitInBytes + " bytes per " +
              requestLimitPeriodInSeconds + " seconds exceeded");
          }
        }
      }
    }
  }
  

View on GitHub (pinned to 2e990059ce)

Solutions

  1. Wait for requestLimitPeriodInSeconds to pass, then resume with reduced volume
  2. Add client-side throttling/backoff: track bytes sent and cap below ipRequestLimitInBytes per window
  3. Use an authenticated Premium account with higher per-IP limits
  4. If you administer the server, raise the IP size limit (ipRequestLimitInBytes) in HTTPServerConfig/launch options
  5. Move the workload to a different IP or distribute requests across allowed clients

Example fix

// before: unbounded loop
for (doc : docs) client.check(doc, Mode.TEXTLEVEL_ONLY);
// after: rate-aware loop
long sent = 0; long windowStart = now();
for (doc : docs) {
  if (now()-windowStart > period) { sent = 0; windowStart = now(); }
  if (sent + doc.length() > ipLimitBytes) sleep(windowStart + period - now());
  client.check(doc, Mode.TEXTLEVEL_ONLY); sent += doc.length();
}
Defensive patterns

Strategy: retry

Validate before calling

function canSend(bytes) { return bytesSentFromThisIpInWindow + bytes <= IP_LIMIT_BYTES; }

Try / catch

try {
  const res = await api.check(text);
} catch (e) {
  if (e.response && e.response.status === 429) { await sleep(periodMs); return api.check(text); }
  throw e;
}

Prevention

When it happens

Trigger: POSTing to /v2/check with mode=textLevelOnly where cumulative request bytes from the client IP exceed ipRequestLimitInBytes within requestLimitPeriodInSeconds (and requestLimitInBytes > 0).

Common situations: Automated scripts or CI jobs behind one IP sending many large requests; multiple users behind a NAT/proxy sharing the quota; server configured with a tight IP size limit (ipRequestLimitInBytes); rapid retry loops amplifying usage.

Related errors


AI-assisted analysis of languagetool-org/languagetool@2e990059ce (2026-09-06). Data as JSON: /api/errors/6cc5098e2efeec57. Report an issue: GitHub.