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

  1. Restart the server including the client IP in the allowedIps list (or remove the restriction if acceptable)
  2. If behind a proxy, ensure the real client IP is forwarded and matching the allowlist
  3. 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

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


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