languagetool-org/languagetool · error · AuthException
Anonymous access is prohibited on this server, please provid
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.
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)
Solutions
- Send authentication with the request (username/password/token parameters or Basic auth header) to obtain a premium UID
- 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
- 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
- Check the server's access policy (anonymous allowed?) before deploying clients
- Always attach Basic auth/credentials for private instances
- Distinguish public api.languagetool.org from self-hosted restricted servers in config
- Test auth flow in CI against a locked-down instance
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
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- Invalid userInfo format, expected 'user:password':
- 'language' parameter missing
- Set only 'text' or 'data' parameter, not both
- Could not parse JSON from 'data' parameter
- 'data' key in JSON requires either 'text' or 'annotation' ke
AI-assisted analysis of languagetool-org/languagetool@2e990059ce (2026-09-06).
Data as JSON: /api/errors/f011a3395bf4b738.
Report an issue: GitHub.