languagetool-org/languagetool · error · BadRequestException
Unknown value '{param}' for parameter 'level'. Valid values:
Error message
Unknown value '{param}' for parameter 'level'. Valid values: {levels} What it means
Parameter validation in ServerTools.getLevel: the 'level' request parameter does not match any known Level name (e.g. default, picky, academic, clarity, ...), so the checking level cannot be resolved.
Source
Thrown at languagetool-server/src/main/java/org/languagetool/server/ServerTools.java:232
level = Level.PICKY;
} else if ("academic".equals(param)) {
level = Level.ACADEMIC;
} else if ("clarity".equals(param)) {
level = Level.CLARITY;
} else if ("professional".equals(param)) {
level = Level.PROFESSIONAL;
} else if ("creative".equals(param)) {
level = Level.CREATIVE;
} else if ("customer".equals(param)) {
level = Level.CUSTOMER;
} else if ("jobapp".equals(param)) {
level = Level.JOBAPP;
} else if ("objective".equals(param)) {
level = Level.OBJECTIVE;
} else if ("elegant".equals(param)) {
level = Level.ELEGANT;
} else {
throw new BadRequestException("Unknown value '" + param + "' for parameter 'level'. Valid values: " +
Arrays.stream(Level.values()).map(k -> k.toString().toLowerCase()).collect(Collectors.joining(", ")));
}
} else {
level = Level.DEFAULT;
}
return level;
}
/**
* Remove user-content from message in case parameters require increased privacy.
* @since 5.0
*/
public static String cleanUserTextFromMessage(String s, Map<String, String> params) {
if (params.getOrDefault("inputLogging", "").equals("no")) {
return sentContentPattern.matcher(s).replaceAll("<< content removed >>");
}
return s;
}View on GitHub (pinned to 2e990059ce)
Solutions
- Use one of the documented lowercase values (see the error message's 'Valid values:' list, e.g. picky)
- Lowercase your value before sending — comparison uses String.equals on lowercase
- Drop the level parameter to get Level.DEFAULT
- Parse the Valid values list from the 400 response to build a client-side enum
Example fix
// before "level=Picky" // after "level=picky"
Defensive patterns
Strategy: validation
Validate before calling
const VALID_LEVELS = ['default','picky','jobapp','objective','elegant'];
function validateLevel(level) {
if (level !== undefined && !VALID_LEVELS.includes(String(level).toLowerCase())) {
throw new Error(`level must be one of ${VALID_LEVELS.join(', ')}`);
}
return level ? String(level).toLowerCase() : undefined;
} Try / catch
try {
return await lt.check({ ...params, level });
} catch (e) {
if (e.status === 400 && /parameter 'level'/.test(e.message)) {
return await lt.check({ ...params, level: undefined });
}
throw e;
} Prevention
- Lowercase the level value client-side before sending (server compares lowercase)
- Derive allowed values from the 'Valid values:' list in the 400 message
- Use a typed enum in client code instead of free-form strings
- Check the LanguageTool API docs for the current Level set when upgrading
When it happens
Trigger: Calling /v2/check with level=<unknown>, e.g. level=formal, level=picky2, level=PICKY (case must be lowercase — code compares lowercase strings), or a typo.
Common situations: Assuming case-insensitivity ('Picky' fails); using level names from other tools or older LanguageTool versions; dynamic UIs passing arbitrary strings straight through.
Understand the failure class
Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.
Related errors
- Mode must be one of 'textLevelOnly', 'allButTextLevelOnly',
- This end point needs a user id
- apiKey AND password was set, set only apiKey
- With 'username' set, you also need to specify 'apiKey'
- apiKey was set, but username was not: {apiKey}
AI-assisted analysis of languagetool-org/languagetool@2e990059ce (2026-09-06).
Data as JSON: /api/errors/55ac21bb4407d8a8.
Report an issue: GitHub.