languagetool-org/languagetool · error · BadRequestException

apiKey AND password was set, set only apiKey

Error message

apiKey AND password was set, set only apiKey

What it means

ServerTools.getUserLimits validates authentication parameters for /v2/check. Passing both apiKey and password with a username is ambiguous (two credential schemes), so it throws BadRequestException (HTTP 400) telling you to send only apiKey.

Source

Thrown at languagetool-server/src/main/java/org/languagetool/server/ServerTools.java:145

  static void setCommonHeaders(HttpExchange httpExchange, String contentType, String allowOriginUrl) {
    httpExchange.getResponseHeaders().set("Content-Type", contentType);
    setAllowOrigin(httpExchange, allowOriginUrl);
  }

  static void setAllowOrigin(HttpExchange httpExchange, String allowOriginUrl) {
    if (allowOriginUrl != null) {
      httpExchange.getResponseHeaders().set("Access-Control-Allow-Origin", allowOriginUrl);
    }
  }

  static UserLimits getUserLimits(Map<String, String> params, HTTPServerConfig config) {
    return getUserLimits(params, config, null);
  }

  static UserLimits getUserLimits(Map<String, String> params, HTTPServerConfig config, String authHeader) {
    if (params.get("username") != null) {
      if (params.get("apiKey") != null && params.get("password") != null) {
        throw new BadRequestException("apiKey AND password was set, set only apiKey");
      }
      if (params.get("apiKey") != null) {
        return UserLimits.getLimitsByApiKey(config, params.get("username"), params.get("apiKey"));
      } else if (params.get("password") != null) {
        return UserLimits.getLimitsFromUserAccount(config, params.get("username"), params.get("password"));
      } else if (params.get("tokenV2") != null) {
        if (authHeader != null) {
          return UserLimits.getLimitsWithJwtToken(config, authHeader, params.get("username"), params.get("tokenV2"));
        }
        return UserLimits.getLimitsByAddonToken(config, params.get("username"), params.get("tokenV2"));
      } else {
        throw new BadRequestException("With 'username' set, you also need to specify 'apiKey'");
      }
    } else {
      if (params.get("apiKey") != null) {
        throw new BadRequestException("apiKey was set, but username was not: " + params.get("apiKey"));
      }
      if (params.get("password") != null) {

View on GitHub (pinned to 2e990059ce)

Solutions

  1. Remove the password parameter and keep username + apiKey
  2. Remove apiKey and keep username + password if you intentionally use password auth (legacy)
  3. Search your client code/config for where both params are injected and make them mutually exclusive

Example fix

// before
params.put("username", user); params.put("apiKey", key); params.put("password", pwd);
// after
params.put("username", user); params.put("apiKey", key); // drop password
Defensive patterns

Strategy: validation

Validate before calling

function validateAuthParams(params) {
  if (params.username && params.apiKey && params.password) {
    throw new Error('Send only apiKey (or only password), not both');
  }
}

Try / catch

try {
  return await lt.check(params);
} catch (e) {
  if (e.status === 400 && /apiKey AND password/.test(e.message)) {
    delete params.password; return await lt.check(params);
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling /v2/check with params username=X, apiKey=Y, AND password=Z — any request containing all three parameters.

Common situations: Migrating from old username/password auth to API keys and leaving the old password param in the client config; template/query builders that always append both fields; copy-pasted example code including both.

Understand the failure class

Background: "mutually exclusive" flag errors: what "can't supply both nx and xx", "--raw is not compatible with -i" and "cannot be used with" mean, and how to fix them — this error's family across 29 libraries.

Related errors


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