languagetool-org/languagetool · error · BadRequestException

Unknown value ' ' 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.

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.

Appendix: 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)