languagetool-org/languagetool · error · RuntimeException
Error: Access from denied
Error message
Error: Access from denied
What it means
Thrown (as a RuntimeException and 403 HTTP response) when the client's IP address is not allowed by the server's access-control configuration. LanguageTool servers can restrict which remote addresses may connect; disallowed IPs are rejected before any API processing.
Source
Thrown at languagetool-server/src/main/java/org/languagetool/server/LanguageToolHttpHandler.java:228
String pathWithoutVersion = path.substring("/v2/".length());
final Map<String, String> finalParameters = parameters;
final String finalRemoteAddress = remoteAddress;
TelemetryProvider.INSTANCE.createSpan("/v2", Attributes.empty(), () -> apiV2.handleRequest(pathWithoutVersion, httpExchange, finalParameters, errorRequestLimiter, finalRemoteAddress, config));
} else if (path.endsWith("/Languages")) {
throw new BadRequestException("You're using an old version of our API that's not supported anymore. Please see " + API_DOC_URL);
} else if (path.equals("/")) {
throw new BadRequestException("Missing arguments for LanguageTool API. Please see " + API_DOC_URL);
} else if (path.contains("/v2/")) {
throw new BadRequestException("You have '/v2/' in your path, but not at the root. Try an URL like 'http://server/v2/...' ");
} else if (path.equals("/favicon.ico")) {
sendError(httpExchange, HttpURLConnection.HTTP_NOT_FOUND, "Not found");
} else {
throw new BadRequestException("This is the LanguageTool API. You have not specified any parameters. Please see " + API_DOC_URL);
}
} else {
String errorMessage = "Error: Access from " + StringTools.escapeXML(origAddress) + " denied";
sendError(httpExchange, HttpURLConnection.HTTP_FORBIDDEN, errorMessage);
throw new RuntimeException(errorMessage);
}
} catch (Exception e) {
String response;
int errorCode;
boolean textLoggingAllowed = false;
boolean logStacktrace = true;
Throwable rootCause = ExceptionUtils.getRootCause(e);
if (e instanceof TextTooLongException || rootCause instanceof TextTooLongException) {
errorCode = HttpURLConnection.HTTP_ENTITY_TOO_LARGE;
response = e.getMessage();
logStacktrace = false;
} else if (e instanceof ErrorRateTooHighException || rootCause instanceof ErrorRateTooHighException) {
errorCode = HttpURLConnection.HTTP_BAD_REQUEST;
response = ExceptionUtils.getRootCause(e).getMessage();
logStacktrace = false;
} else if (hasCause(e, AuthException.class)) {
errorCode = HttpURLConnection.HTTP_FORBIDDEN;
response = AuthException.class.getName() + ": " + e.getMessage();View on GitHub (pinned to 2e990059ce)
Solutions
- Restart the server including the client IP in the allowedIps list (or remove the restriction if acceptable)
- If behind a proxy, ensure the real client IP is forwarded and matching the allowlist
- Verify the client's egress IP and update firewall/proxy configuration accordingly
Example fix
// before java -cp languagetool-server.jar org.languagetool.server.HTTPServer --port 8081 --allowedIps 127.0.0.1 // after java -cp languagetool-server.jar org.languagetool.server.HTTPServer --port 8081 --allowedIps 127.0.0.1,203.0.113.7
Defensive patterns
Strategy: try-catch
Validate before calling
// confirm client egress IP against the server's allowedIps before calling
console.log('Egress IP:', await fetch('https://api.ipify.org').then(r => r.text())); Try / catch
try {
const res = await fetch(url, opts);
if (res.status === 403) throw new Error(`IP not allowed by server: ${ip}`);
} catch (e) {
// alert ops to update --allowedIps or proxy IP forwarding
} Prevention
- Keep allowedIps in sync with all legitimate egress IPs (NAT, LB, k8s nodes)
- Use X-Forwarded-For-aware setups when behind proxies
- Document IP allowlist requirements for deployments
When it happens
Trigger: Request originates from an IP not on the server's allowlist (configured via --allowedIps or the access-control config), so handle() takes the deny branch.
Common situations: Server started with an allowedIps restriction and a legitimate client connects from an unexpected address, connections through a load balancer/NAT whose forwarded IP isn't allowlisted, or Kubernetes/Docker networking exposing a different source IP.
Understand the failure class
Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.
Related errors
- Missing 'text' or 'data' parameter
- Use parameter 'dicts', not 'dict' in GET /words API method.
- 'lang' parameter missing
- 'ruleId' parameter missing
- Rule '<ruleId>' not found for language <lang> (LanguageTool
AI-assisted analysis of languagetool-org/languagetool@2e990059ce (2026-09-06).
Data as JSON: /api/errors/077c540760c2e3ff.
Report an issue: GitHub.