languagetool-org/languagetool · error · BadRequestException
With 'username' set, you also need to specify 'apiKey'
Error message
With 'username' set, you also need to specify 'apiKey'
What it means
When the 'username' parameter is present, getUserLimits requires one of apiKey, password, or tokenV2 to authenticate the user. If none is provided it throws BadRequestException (HTTP 400) asking for 'apiKey'.
Source
Thrown at languagetool-server/src/main/java/org/languagetool/server/ServerTools.java:157
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) {
throw new BadRequestException("password was set, but username was not");
}
if (authHeader != null) {
return UserLimits.getLimitsWithJwtToken(config, authHeader, params.get("username"), params.get("tokenV2"));
}
return UserLimits.getDefaultLimits(config);
}
}
@NotNull
static Mode getMode(Map<String, String> params) {
Mode mode;View on GitHub (pinned to 2e990059ce)
Solutions
- Add the apiKey parameter alongside username
- Alternatively provide password (legacy) or tokenV2 (addon token) with the username
- If you do not need user limits, remove the username parameter to fall back to default/IP limits
Example fix
// before POST /v2/check?username=alice&text=... // after POST /v2/check?username=alice&apiKey=SECRET&text=...
Defensive patterns
Strategy: validation
Validate before calling
function validateAuthParams(params) {
if (params.username && !params.apiKey && !params.password && !params.tokenV2) {
throw new Error("'username' requires 'apiKey' (or password/tokenV2)");
}
} Try / catch
try {
return await lt.check(params);
} catch (e) {
if (e.status === 400 && /also need to specify 'apiKey'/.test(e.message)) {
throw new ConfigError('LT_USERNAME set without LT_API_KEY');
}
throw e;
} Prevention
- Always pair username with apiKey in your credential store
- Fail fast at startup if only one of the pair is configured
- Omit username entirely when using anonymous/IP-based limits
When it happens
Trigger: Calling /v2/check with username=... but no apiKey, no password, and no tokenV2 parameter.
Common situations: Adding a username for limits but forgetting the credential; stripping credentials during config refactor; sending username in the URL and expecting the API key in a header the server does not read here.
Understand the failure class
Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.
Related errors
- apiKey was set, but username was not: {apiKey}
- password was set, but username was not
- apiKey AND password was set, set only apiKey
- Expected Basic Authentication
- This end point needs a user id
AI-assisted analysis of languagetool-org/languagetool@2e990059ce (2026-09-06).
Data as JSON: /api/errors/e328fe9ad711654e.
Report an issue: GitHub.