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

  1. Use one of the documented lowercase values (see the error message's 'Valid values:' list, e.g. picky)
  2. Lowercase your value before sending — comparison uses String.equals on lowercase
  3. Drop the level parameter to get Level.DEFAULT
  4. 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

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


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