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

  1. Remove the extra segments; move auxiliary metadata to a separate column
  2. Use BCP 47 language tags ('en-US') which are routed to Locale.Builder.setLanguageTag and handle extensions correctly
  3. Validate with Locale.forLanguageTag(...)/Locale.Builder before saving
  4. 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

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


AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22). Data as JSON: /api/errors/bd229ec430018ee8. Report an issue: GitHub.