jwtk/jjwt · error · IllegalArgumentException

Locale part "" contains invalid characters

Error message

Locale part "" contains invalid characters

What it means

Thrown by Strings.validateLocalePart when a component of a locale string (language, country, or variant) contains characters other than letters, digits, underscore, or space. JJWT validates locale parts before constructing a java.util.Locale to reject malformed values early.

Solutions

  1. Remove invalid characters from the locale part; use only letters, digits, '_' and ' '
  2. Split combined tags into language/country/variant parts and pass each part separately
  3. Replace hyphens with underscores if converting from BCP-47: "en-US" -> "en_US"
  4. Catch IllegalArgumentException and fall back to a default Locale

Example fix

// before
Locale locale = new Locale("en-US"); // hyphen rejected
// after
Locale locale = Locale.forLanguageTag("en-US"); // or new Locale("en", "US")
Defensive patterns

Strategy: validation

Validate before calling

String part = localePart == null ? "" : localePart;
for (char ch : part.toCharArray()) {
    if (ch != '_' && ch != ' ' && !Character.isLetterOrDigit(ch)) throw new IllegalArgumentException("Invalid locale char: " + ch);
}

Try / catch

try {
    locale = buildLocale(raw);
} catch (IllegalArgumentException e) {
    locale = Locale.ROOT; // safe default
}

Prevention

When it happens

Trigger: Calling a JJWT API that takes a locale string (e.g. in builders for locale-aware claims or Locale strings) with a part containing characters like '-', '#', '.', or other punctuation.

Common situations: Passing BCP-47 style tags with hyphens ("en-US") where underscore form is expected, or locale strings polluted by config file syntax or URL encoding.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


AI-assisted analysis of jwtk/jjwt@fb71496164 (2026-09-09). Data as JSON: /api/errors/4e0740dbaeb2eb2e. Report an issue: GitHub.

Appendix: source

Thrown at api/src/main/java/io/jsonwebtoken/lang/Strings.java:874

        String variant = "";
        if (parts.length >= 2) {
            // There is definitely a variant, and it is everything after the country
            // code sans the separator between the country code and the variant.
            int endIndexOfCountryCode = localeString.indexOf(country) + country.length();
            // Strip off any leading '_' and whitespace, what's left is the variant.
            variant = trimLeadingWhitespace(localeString.substring(endIndexOfCountryCode));
            if (variant.startsWith("_")) {
                variant = trimLeadingCharacter(variant, '_');
            }
        }
        return (language.length() > 0 ? new Locale(language, country, variant) : null);
    }

    private static void validateLocalePart(String localePart) {
        for (int i = 0; i < localePart.length(); i++) {
            char ch = localePart.charAt(i);
            if (ch != '_' && ch != ' ' && !Character.isLetterOrDigit(ch)) {
                throw new IllegalArgumentException("Locale part \"" + localePart + "\" contains invalid characters");
            }
        }
    }

    /**
     * Determine the RFC 3066 compliant language tag,
     * as used for the HTTP "Accept-Language" header.
     *
     * @param locale the Locale to transform to a language tag
     * @return the RFC 3066 compliant language tag as String
     */
    public static String toLanguageTag(Locale locale) {
        return locale.getLanguage() + (hasText(locale.getCountry()) ? "-" + locale.getCountry() : "");
    }


    //---------------------------------------------------------------------
    // Convenience methods for working with String arrays

View on GitHub (pinned to fb71496164)