languagetool-org/languagetool · error · BadRequestException
apiKey was set, but username was not: {apiKey}
Error message
apiKey was set, but username was not: {apiKey} What it means
Parameter validation in ServerTools.getUserLimits: an apiKey was supplied in the request parameters without a username, so the API-key-based user limits lookup cannot identify the user.
Source
Thrown at languagetool-server/src/main/java/org/languagetool/server/ServerTools.java:161
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;
if (params.get("mode") != null) {
String modeParam = params.get("mode");
if ("textLevelOnly".equals(modeParam)) {
mode = Mode.TEXTLEVEL_ONLY;View on GitHub (pinned to 2e990059ce)
Solutions
- Add the matching username parameter next to apiKey
- Verify the username is the account the API key was issued for
- If your tooling only supports an apiKey, enable it to also send the associated username (LanguageTool requires the pair)
Example fix
// before
params.put("apiKey", apiKey);
// after
params.put("apiKey", apiKey); params.put("username", username); Defensive patterns
Strategy: validation
Validate before calling
function validateAuthParams(params) {
if (params.apiKey && !params.username) {
throw new Error("'apiKey' requires 'username'");
}
} Try / catch
try {
return await lt.check(params);
} catch (e) {
if (e.status === 400 && /apiKey was set, but username was not/.test(e.message)) {
params.username = getUsernameFor(params.apiKey); return await lt.check(params);
}
throw e;
} Prevention
- Store apiKey and its username together (keyed credential records)
- Never log or embed the apiKey in errors — the server echoes it in the message
- Validate the credential pair before constructing requests
When it happens
Trigger: Calling /v2/check (or another endpoint that runs getUserLimits) with apiKey=... but no username parameter.
Common situations: Sending only the API key assuming it is self-contained (keys are checked with username in this server); losing the username during env-var/config propagation; client libraries that only expose an apiKey option.
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
- With 'username' set, you also need to specify '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/81cdf1f8f27eb3a3.
Report an issue: GitHub.