{"record":{"id":"757d5e37db9269bd","repo":"hibernate/hibernate-orm","slug":"invalid-extension-s","errorCode":null,"errorMessage":"Invalid extension: %s","messagePattern":"Invalid extension: (.+?)","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/type/descriptor/java/LocaleJavaType.java","lineNumber":167,"sourceCode":"\t\t\t\tcase EXTENSION -> {\n\t\t\t\t\thandleExtension(  chars, start, length, builder );\n\t\t\t\t\tyield END;\n\t\t\t\t}\n\t\t\t\tcase END -> throw new IllegalStateException( \"Unexpected continuation of locale value after extension: \" + new String( chars, start, length ) );\n\t\t\t};\n\t\t}\n\n\t\tprivate boolean isScript(char[] chars, int start, int length) {\n\t\t\treturn length == 4\n\t\t\t\t&& isLetter( chars[start] )\n\t\t\t\t&& isLetter( chars[start + 1] )\n\t\t\t\t&& isLetter( chars[start + 2] )\n\t\t\t\t&& isLetter( chars[start + 3] );\n\t\t}\n\n\t\tprivate void handleExtension(char[] chars, int start, int length, Locale.Builder builder) {\n\t\t\tif ( length < 3 || chars[start + 1] != '-' ) {\n\t\t\t\tthrow new IllegalArgumentException( \"Invalid extension: \" + new String( chars, start, length ) );\n\t\t\t}\n\t\t\tif ( toLowerCase( chars[start] ) == 'u' ) {\n\t\t\t\t// After a Unicode extension, there could come a private use extension which we need to detect\n\t\t\t\tint unicodeStart = start + 2;\n\t\t\t\tint unicodeLength = length - 2;\n\t\t\t\tfinal int end = start + length;\n\t\t\t\tfor ( int i = start + 2; i < end; i++ ) {\n\t\t\t\t\tif ( chars[i] == '-' && i + 3 < end && chars[i + 1] == 'x' && chars[i + 2] == '-' ) {\n\t\t\t\t\t\tbuilder.setExtension( 'x', new String( chars, i + 3, end - i - 3 ) );\n\t\t\t\t\t\tunicodeLength = i - unicodeStart;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbuilder.setExtension( chars[start], new String( chars, unicodeStart, unicodeLength ) );\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbuilder.setExtension( chars[start], new String( chars, start + 2, length - 2 ) );\n\t\t\t}","sourceCodeStart":149,"sourceCodeEnd":185,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/LocaleJavaType.java#L149-L185","documentation":"handleExtension() in LocaleJavaType validates the extension segment of a legacy locale value: it must be at least 3 characters long and its second character must be '-' (shape 'k-...' like 'u-ca-gregory' or 'x-privatedata'). It is reached for the segment after a '#script' part, for a '#'-segment that is not a valid 4-letter script, or after an extension in state EXTENSION. Anything malformed throws IllegalArgumentException(\"Invalid extension: ...\").","triggerScenarios":"'#'-segments that are not 4 letters get treated as extensions and fail, e.g. 'en_US_var#ca-gregory' (second char 'a', not '-'); extension segments shorter than 3 chars, e.g. '..._u'; BCP 47 extensions written with underscores like 'en_US_var#Latn_u_ca_gregory' (inner underscores split the segment so chars[start+1] is not '-')","commonSituations":"Converting BCP 47 tags to underscore format by naive replace('-','_'); hand-built locale strings in seed data; copy/paste from documentation examples with wrong separators.","solutions":["Store BCP 47 language tags with '-' separators; the parser then uses setLanguageTag and full extension syntax works","For legacy form, use exactly one underscore between segments and dashes inside the extension ('en_US_var#Latn_u-ca-gregory')","Normalize inbound strings with Locale.Builder before persisting","Fix seed/import data that contains underscore-separated extension keywords"],"exampleFix":"// before\nString stored = \"en_US_var#Latn_u_ca_gregory\"; // Invalid extension: u\n\n// after\nString stored = \"en-US-u-ca-gregory\"; // BCP 47\n// legacy alternative: \"en_US_var#Latn_u-ca-gregory\"","handlingStrategy":"validation","validationCode":"static boolean looksLikeLegacyExtension(String seg) {\n    return seg.length() >= 3 && seg.charAt(1) == '-';\n}\n\n// validate '#'-segments before persisting underscore-format locales\nstatic boolean validSegment(String seg) {\n    return seg.length() == 4 && seg.chars().allMatch(Character::isLetter) // script\n        || looksLikeLegacyExtension(seg);                                  // 'u-ca-...'\n}","typeGuard":null,"tryCatchPattern":"catch (IllegalArgumentException e) {\n    // message starts with \"Invalid extension: \"\n    throw new IllegalArgumentException(\"Extension must look like 'k-value' (dash at index 1): \" + value, e);\n}","preventionTips":["Use '-' inside extensions and '_' between legacy segments","Store BCP 47 tags to avoid the hand-rolled grammar entirely","Test locale strings through LocaleJavaType.fromString in unit tests for seed data"],"tags":["hibernate","locale","parsing","extension","illegalargumentexception"],"backgroundTag":"invalid-locale-format","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}