languagetool-org/languagetool · error · AuthException

This is the endpoint for the basic version of LanguageTool.

Error message

This is the endpoint for the basic version of LanguageTool. When using 'username' and 'apiKey' to access the premium version, use api.languagetoolplus.com instead.

What it means

When sqlSessionFactory is null, the open-source implementation has no database configured, so getUserId throws AuthException telling the caller that this is the basic LanguageTool endpoint and username/apiKey (premium) auth belongs on api.languagetoolplus.com. It is a routing/configuration error: credentials were provided to a server that cannot process them.

Source

Thrown at languagetool-server/src/main/java/org/languagetool/server/DatabaseAccessOpenSource.java:212

        Date now = new Date();
        map.put("created_at", now);
        map.put("updated_at", now);
        int affectedRows = session.insert("org.languagetool.server.UserDictMapper.addWord", map);
        logger.info("Added '" + word + "' for user " + userId + " to list of ignored words, affectedRows: " + affectedRows);
        return affectedRows == 1;
      }
    }
  }

  Long getUserId(String username, String apiKey) {
    if (username == null || username.trim().isEmpty()) {
      throw new IllegalArgumentException("username must be set");
    }
    if (apiKey == null || apiKey.trim().isEmpty()) {
      throw new IllegalArgumentException("apiKey must be set");
    }
    if (sqlSessionFactory == null) {
      throw new AuthException("This is the endpoint for the basic version of LanguageTool. " +
        "When using 'username' and 'apiKey' to access the premium version, use api.languagetoolplus.com instead.");
    }
    try {
      Long value = dbLoggingCache.get(String.format("user_%s_%s", username, apiKey), () -> {
        try (SqlSession session = sqlSessionFactory.openSession()) {
          Map<Object, Object> map = new HashMap<>();
          map.put("username", username);
          map.put("apiKey", apiKey);
          Long id = session.selectOne("org.languagetool.server.UserDictMapper.getUserIdByApiKey", map);
          if (id == null) {
            return -1L;
          }
          return id;
        }
      });
      if (value == -1) {
        throw new IllegalArgumentException("No user found for given username '" + username + "' and given api key");
      } else {

View on GitHub (pinned to 2e990059ce)

Solutions

  1. If you have a premium subscription, change the client endpoint to https://api.languagetoolplus.com.
  2. If self-hosting with your own users, start the server with database configuration so sqlSessionFactory is initialized (premium setup required for the bundled DB support).
  3. Remove username/apiKey from the request when using the basic open-source server (use anonymous or OSS-supported auth).

Example fix

// before
POST http://localhost:8081/v2/check  { "username":"bob", "apiKey":"k" } // AuthException
// after
POST https://api.languagetoolplus.com/v2/check  { "username":"bob", "apiKey":"k" }
// or, on the basic server, omit username/apiKey entirely
Defensive patterns

Strategy: validation

Validate before calling

if (usesUsernameApiKeyAuth && !isPremiumEndpoint(baseUrl)) {
  throw new IllegalArgumentException("username/apiKey only valid on api.languagetoolplus.com or a DB-configured server");
}

Type guard

boolean isPremiumEndpoint(String url) { return url != null && url.contains("languagetoolplus.com"); }

Try / catch

try {
  response = api.check(text, username, apiKey);
} catch (AuthException e) {
  // retry without credentials or redirect to premium endpoint
}

Prevention

When it happens

Trigger: Sending username + apiKey authentication to an open-source languagetool-server that was started WITHOUT database configuration (no MyBatis/sqlSessionFactory), i.e. any request hitting getUserId on a basic self-hosted instance.

Common situations: Self-hosting LanguageTool and passing premium credentials (username/apiKey) that only work on the paid service; a server started without --database* config options receiving premium-style auth.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


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