languagetool-org/languagetool · error · IllegalArgumentException

code is supposed to be a 2 (or rarely 3) character code (unl

Error message

code is supposed to be a 2 (or rarely 3) character code (unless it uses a format with variant, like xx-YY): ''

What it means

LanguageTool's server supports adding custom 'dynamic' languages at startup via properties like `lang-xx` in the server config. This error is thrown when the code extracted from a `lang-*` property key is not a 2- or 3-character language code (and contains no '-' for variant forms like xx-YY). It fails fast so a misconfigured custom language is caught before the server starts serving.

Source

Thrown at languagetool-server/src/main/java/org/languagetool/server/HTTPServerConfig.java:532

          File dir = new File(ngramLangIdentData);
          if (!dir.exists() || dir.isDirectory()) {
            throw new IllegalArgumentException("ngramLangIdentData does not exist or is a directory (needs to be a ZIP file): " + ngramLangIdentData);
          }
          setNgramLangIdentData(dir);
        }
      }
    } catch (IOException e) {
      throw new RuntimeException("Could not load properties from '" + file + "'", e);
    }
  }

  private void addDynamicLanguages(Properties props) throws IOException {
    for (Object keyObj : props.keySet()) {
      String key = (String)keyObj;
      if (key.startsWith("lang-") && !key.contains("-dictPath")) {
        String code = key.substring("lang-".length());
        if (!code.contains("-") && code.length() != 2 && code.length() != 3) {
          throw new IllegalArgumentException("code is supposed to be a 2 (or rarely 3) character code (unless it uses a format with variant, like xx-YY): '" + code + "'");
        }
        String nameKey = "lang-" + code;
        String name = props.getProperty(nameKey);
        String dictPathKey = "lang-" + code + "-dictPath";
        String dictPath = props.getProperty(dictPathKey);
        if (dictPath == null) {
          throw new IllegalArgumentException(dictPathKey + " must be set");
        }
        File dictPathFile = new File(dictPath);
        if (!dictPathFile.exists() || !dictPathFile.isFile()) {
          throw new IllegalArgumentException("dictionary file does not exist or is not a file: '" + dictPath + "'");
        }
        ServerTools.print("Adding dynamic spell checker language " + name + ", code: " + code + ", dictionary: " + dictPath);
        Language lang = Languages.addLanguage(name, code, new File(dictPath));
        // better fail early in case of misconfiguration, so use the language now:
        if (!new File(lang.getCommonWordsPath()).exists()) {
          throw new IllegalArgumentException("Common words path not found: '" + lang.getCommonWordsPath() + "'");
        }

View on GitHub (pinned to 2e990059ce)

Solutions

  1. Rename the property key so the part after `lang-` is a 2- or 3-character ISO 639-1/639-3 code, e.g. `lang-de=German`
  2. If you need a regional variant, use the hyphenated form like `lang-de-DE` (contains '-', so length check is skipped)
  3. Remove the stray `lang-` prefixed key if it was not intended to define a custom language

Example fix

// before
lang-french=French
// after
lang-fr=French
Defensive patterns

Strategy: validation

Validate before calling

for (String key : props.stringPropertyNames()) {
  if (key.startsWith("lang-")) {
    String code = key.substring("lang-".length());
    if (!code.contains("-") && code.length() != 2 && code.length() != 3)
      throw new IllegalStateException("Invalid dynamic language code in key: " + key);
  }
}

Type guard

boolean isValidLangCode(String code) {
  return code != null && (code.contains("-") || code.length() == 2 || code.length() == 3);
}

Prevention

When it happens

Trigger: A properties file passed via --config contains a key starting with `lang-` whose suffix (the part after `lang-`) is empty, 1 character, or longer than 3 characters and contains no hyphen, e.g. `lang-french=French` or `lang-=...`.

Common situations: Admins write the language NAME instead of the ISO code in the property key (`lang-german`), typos in the code (`lang-deutch`), or an empty key suffix after `lang-`.

Understand the failure class

Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.

Related errors


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