languagetool-org/languagetool · error · TooManyRequestsException
Client request limit of requests per seconds exceeded
Error message
Client request limit of requests per seconds exceeded
What it means
HTTP 429 error thrown when requests sharing the same client fingerprint exceed the per-client request limit within the rate-limit period. This complements the per-IP limit and applies when ipFingerprintFactor and requestLimit are positive.
Source
Thrown at languagetool-server/src/main/java/org/languagetool/server/RequestLimiter.java:219
float modeFactor = event.mode == JLanguageTool.Mode.TEXTLEVEL_ONLY ? 0.1f : 1f;
requestsByIp++;
requestSizeByIp += event.getSizeInBytes() * modeFactor;
if (whitelistedUser) {
if (whitelistLimit <= 0 || requestsByIp < whitelistLimit) {
continue;
} else {
String msg = "limit: " + ipRequestLimit + " / " + requestLimitPeriodInSeconds + ", requests: " + requestsByIp + ", ip: " + ipAddress + ", fingerprint: " + fingerprint;
throw new TooManyRequestsException("Whitelist request limit of " + whitelistLimit + " requests per " +
requestLimitPeriodInSeconds + " seconds exceeded");
}
}
if (event.fingerprint.equals(fingerprint)) {
requestsByFingerprint++;
requestSizeByFingerprint += event.getSizeInBytes() * modeFactor;
}
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 {View on GitHub (pinned to 2e990059ce)
Solutions
- Add client-side throttling: queue requests and keep under requestLimit per period
- Increase the server's client requestLimit configuration
- Stagger retries with exponential backoff and jitter instead of tight retry loops
Defensive patterns
Strategy: retry
Validate before calling
class Limiter {
constructor(max, periodMs) { this.max = max; this.periodMs = periodMs; this.hits = []; }
async acquire() {
const now = Date.now();
this.hits = this.hits.filter(t => now - t < this.periodMs);
if (this.hits.length >= this.max) throw new Error('Client rate limit would be exceeded');
this.hits.push(now);
}
} Try / catch
try {
return await check(text);
} catch (e) {
if (e.status === 429 && /Client request limit/.test(e.message)) {
await backoff(attempt); // exponential backoff with jitter
return check(text);
}
throw e;
} Prevention
- Serialize/throttle requests from each client instance
- Add jitter to retries to avoid thundering herds
- Monitor request rates per client token
When it happens
Trigger: checkLimit detects requestsByFingerprint > requestLimit for one fingerprint within requestLimitPeriodInSeconds.
Common situations: A single application instance (same fingerprint, e.g. same client token) sending high-volume traffic, parallel worker threads without throttling, or aggressive retry loops amplifying request counts.
Related errors
- User request limit of per day exceeded. Try again after mid
- Whitelist request limit of requests per seconds exceeded
- IP request limit of requests per seconds exceeded
- Client request size limit of bytes per seconds exceeded in
- IP request size limit of bytes per seconds exceeded in tex
AI-assisted analysis of languagetool-org/languagetool@2e990059ce (2026-09-06).
Data as JSON: /api/errors/83fa6b966207b1d3.
Report an issue: GitHub.