hibernate/hibernate-orm · error · IllegalStateException
Unexpected continuation of locale value after extension: %s
Error message
Unexpected continuation of locale value after extension: %s
What it means
The locale parsing state machine reaches state END after an extension segment has been consumed; the grammar allows nothing after language_region_variant#Script_extension. If another underscore-separated segment follows, parseNext throws IllegalStateException("Unexpected continuation of locale value after extension: ..."). It indicates the stored locale value has more segments than the format supports, not a transient condition.
Source
Thrown at hibernate-core/src/main/java/org/hibernate/type/descriptor/java/LocaleJavaType.java:153
}
case SCRIPT -> {
if ( length < 5 || chars[start] != '#' ) {
throw new IllegalArgumentException( "Invalid script: " + new String( chars, start, length ) );
}
if ( isScript( chars, start + 1, length - 1 ) ) {
builder.setScript( new String( chars, start + 1, length - 1 ) );
yield EXTENSION;
}
else {
handleExtension( chars, start + 1, length - 1, builder );
yield END;
}
}
case EXTENSION -> {
handleExtension( chars, start, length, builder );
yield END;
}
case END -> throw new IllegalStateException( "Unexpected continuation of locale value after extension: " + new String( chars, start, length ) );
};
}
private boolean isScript(char[] chars, int start, int length) {
return length == 4
&& isLetter( chars[start] )
&& isLetter( chars[start + 1] )
&& isLetter( chars[start + 2] )
&& isLetter( chars[start + 3] );
}
private void handleExtension(char[] chars, int start, int length, Locale.Builder builder) {
if ( length < 3 || chars[start + 1] != '-' ) {
throw new IllegalArgumentException( "Invalid extension: " + new String( chars, start, length ) );
}
if ( toLowerCase( chars[start] ) == 'u' ) {
// After a Unicode extension, there could come a private use extension which we need to detect
int unicodeStart = start + 2;View on GitHub (pinned to fad1729dce)
Solutions
- Remove the extra segments; move auxiliary metadata to a separate column
- Use BCP 47 language tags ('en-US') which are routed to Locale.Builder.setLanguageTag and handle extensions correctly
- Validate with Locale.forLanguageTag(...)/Locale.Builder before saving
- Segment-count check on write: at most 4 underscore-separated parts for the legacy grammar
Example fix
// before String stored = "en_US_posix#Latn_u-ca-gregory_backup"; // IllegalStateException // after String stored = "en-US-u-ca-gregory"; // proper BCP 47 tag // metadata 'backup' moved to its own column
Defensive patterns
Strategy: validation
Validate before calling
static boolean hasAtMostFourUnderscoreSegments(String s) {
return s.chars().filter(c -> c == '_').count() <= 3;
}
// legacy grammar: lang_region_variant#script-ext -> max 3 underscores outside '#'
if (!hasAtMostFourUnderscoreSegments(value)) reject(value); Try / catch
catch (IllegalStateException e) {
// "Unexpected continuation of locale value after extension"
throw new IllegalArgumentException("Too many segments in locale value: " + value, e);
} Prevention
- Never append suffixes/metadata to locale codes; use a separate column
- Round-trip validate stored locales after bulk imports
- Prefer BCP 47 tags which fail fast and clearly via Locale.Builder
When it happens
Trigger: Values like 'en_US_var#Latn_u-ca-gregory_extra' (extra segment after the extension); tooling that appends suffixes ('_backup', '_v2') to stored locale codes; double-appending extensions by string concatenation ('..._u-ca-gregory_x-foo_more').
Common situations: Locale columns abused to carry extra metadata; data migrations that concatenated fields; admin tools letting free-form strings into the column.
Related errors
- Invalid script: %s
- Invalid extension: %s
- Unexpected PostgreSQL lock_timeout format: {}
- Could not find root
- Cannot parse given string into array of strings. First and l
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/bd229ec430018ee8.
Report an issue: GitHub.