languagetool-org/languagetool · error · AuthException

Anonymous access is prohibited on this server, please…

Error message

Anonymous access is prohibited on this server, please provide authentication.

What it means

The server is configured with a limit (e.g. -X 'maximum text length' / anonymousAccess disallowed) that forbids unauthenticated requests. checkText throws AuthException when no premium UID (from authentication) is supplied and the config disallows anonymous access.

Solutions

  1. Send authentication with the request (username/password/token parameters or Basic auth header) to obtain a premium UID
  2. Start the server with anonymous access allowed (e.g. do not set the 'no anonymous' option / set the option permitting public access) if this is intended to be a public server
  3. Point the client at a server instance whose access policy matches your use case

Example fix

// before
curl -d text=... -d language=en-US http://server/v2/check
// after
curl -u user:token -d text=... -d language=en-US http://server/v2/check
Defensive patterns

Strategy: validation

Validate before calling

if (!credentials.username || !credentials.token) {
  throw new Error('This server requires authentication; supply username/token before calling /v2/check');
}

Type guard

function hasAuth(c) {
  return c != null && typeof c === 'object' && typeof c.username === 'string' && c.username.length > 0 && typeof c.token === 'string' && c.token.length > 0;
}

Try / catch

try {
  return await check(text, { auth });
} catch (e) {
  if (e.status === 401 || /Anonymous access is prohibited/.test(e.message)) {
    throw new AuthRequiredError('Configure credentials for this LanguageTool server');
  }
  throw e;
}

Prevention

When it happens

Trigger: POSTing to /v2/check (directly or via testPipelineCreatedAndUsed/testJSONP-style callers) without valid credentials (username/password, API token) on a server started without the anonymousAccess option, so limits.getPremiumUid() is null and config.isAnonymousAccessAllowed() is false.

Common situations: Running a private LanguageTool server with authentication enforced but calling it from a script or browser app that never sends the Basic auth header; CI tests hitting a locked-down production instance.

Understand the failure class

Related errors


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

Appendix: source

Thrown at languagetool-server/src/main/java/org/languagetool/server/TextChecker.java:405

      httpExchange.getResponseBody().write(response.getBytes(ENCODING));
      return;
    }

    String requestId = httpExchange.getRequestHeaders().getFirst("X-Request-ID");

    // logging information
    String agent = params.get("useragent") != null ? params.get("useragent") : "-";
    Long agentId = null, userId = null;
    if (databaseLogger.isLogging()) {
      DatabaseAccess db = DatabaseAccess.getInstance();
      agentId = db.getOrCreateClientId(params.get("useragent"));
      userId = limits.getPremiumUid();
    }
    String referrer = httpExchange.getRequestHeaders().getFirst("Referer");
    String userAgent = httpExchange.getRequestHeaders().getFirst("User-Agent");

    if (!config.isAnonymousAccessAllowed() && limits.getPremiumUid() == null) {
      throw new AuthException("Anonymous access is prohibited on this server, please provide authentication.");
    }

    int length = aText.getPlainText().length();
    if ("true".equals(params.get("languageChanged"))) {
      log.info("languageChanged to " + params.get("language") + " for text with length " + aText.getPlainText().trim().length());
    }
    if (length > limits.getMaxTextLength()) {
      ServerMetricsCollector.getInstance().logRequestError(ServerMetricsCollector.RequestErrorType.MAX_TEXT_SIZE);
      throw new TextTooLongException("Your text exceeds the limit of " + limits.getMaxTextLength() +
              " characters (it's " + length + " characters). Please submit a shorter text.");
    }
    // static because we can't rely on errorRequestLimiter, null when timeoutRequestLimit option not set
    if (!config.isLocalApiMode()) {
      try {
        RequestLimiter.checkUserLimit(referrer, userAgent, limits);
      } catch (TooManyRequestsException e) {
        String response = "Error: Access denied: " + e.getMessage();
        httpExchange.sendResponseHeaders(HttpURLConnection.HTTP_FORBIDDEN, response.getBytes(ENCODING).length);

View on GitHub (pinned to 2e990059ce)