hibernate/hibernate-orm · error · IllegalArgumentException
XML not properly formatted:
Error message
XML not properly formatted:
What it means
XmlHelper.fromString expects an XML aggregate column to hold Hibernate's exact format: a root element <e> ... </e> (or <e/> for null). If the string does not start with <e> and end with </e>, reading the aggregate fails with this IllegalArgumentException. The format is Hibernate-internal, not arbitrary XML.
Source
Thrown at hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/XmlHelper.java:287
}
public static <X> X fromString(
EmbeddableMappingType embeddableMappingType,
String string,
boolean returnEmbeddable,
WrapperOptions options) throws SQLException {
if ( NULL_TAG.equals( string ) ) {
return null;
}
int contentEnd = string.length() - 1;
while ( contentEnd >= 0 ) {
if ( !Character.isWhitespace( string.charAt( contentEnd ) ) ) {
break;
}
contentEnd--;
}
if ( !string.startsWith( START_TAG ) || !string.regionMatches( contentEnd - END_TAG.length()+ 1, END_TAG, 0, END_TAG.length() ) ) {
throw new IllegalArgumentException( "XML not properly formatted: " + string );
}
int end;
final Object[] array;
if ( embeddableMappingType == null ) {
assert !returnEmbeddable;
final List<Object> values = new ArrayList<>( 8 );
end = fromString( string, values, START_TAG.length() );
array = values.toArray();
}
else {
array = new Object[embeddableMappingType.getJdbcValueCount() + ( embeddableMappingType.isPolymorphic() ? 1 : 0 )];
end = fromString( embeddableMappingType, string, returnEmbeddable, options, array, START_TAG.length() );
}
assert end + END_TAG.length() == contentEnd + 1;
if ( returnEmbeddable ) {
final StructAttributeValues attributeValues = StructHelper.getAttributeValues( embeddableMappingType, array, options );
//noinspection uncheckedView on GitHub (pinned to fad1729dce)
Solutions
- Rewrite column data into the <e>...</e> aggregate format (or null it) with a migration script.
- Write values through Hibernate once so the column converges on the correct format.
- Verify the mapping declares the intended aggregate type (@JdbcTypeCode(SqlTypes.SQLXML) or the dialect XML type) matching the column.
Example fix
-- PostgreSQL: wrap stray content into Hibernate's root tag UPDATE t SET xml_col = '<e>' || xml_col || '</e>' WHERE xml_col IS NOT NULL AND xml_col <> '<e/>' AND NOT (xml_col LIKE '<e>%' AND xml_col LIKE '%</e>');
Defensive patterns
Strategy: validation
Validate before calling
static boolean isHibernateAggregateXml(String s) {
return s == null || "<e/>".equals(s)
|| (s.startsWith("<e>") && s.endsWith("</e>") && s.length() >= 7);
}
// audit rows: SELECT id FROM t WHERE NOT (xml_col IS NULL OR xml_col = '<e/>'
// OR (xml_col LIKE '<e>%' AND xml_col LIKE '%</e>')); Try / catch
try {
return session.find(Person.class, id);
} catch (IllegalArgumentException ex) {
if (ex.getMessage() != null && ex.getMessage().startsWith("XML not properly formatted")) {
quarantine(id, ex.getMessage());
return null;
}
throw ex;
} Prevention
- Validate column format with a SQL audit before switching a table to XML aggregate mappings.
- Never let external tools write raw XML into aggregate columns.
- Use NULL (or '<e/>') for empty values instead of empty strings.
When it happens
Trigger: Loading an entity with an XML-mapped embeddable when the column content lacks the <e>...</e> wrapper: arbitrary XML written by another tool, an empty or padded string, or content in a different aggregate format (e.g. a JSON aggregate value left in the column).
Common situations: Column pre-populated by external writers; switching a column between JSON and XML aggregate mappings; data exported from another ORM; leftover test data.
Related errors
- Illegal XML content:
- Illegal XML for array:
- XML not properly formed:
- XML starts sub-object for a non-aggregate type at index %d.
- Support for attribute mapping type not yet implemented:
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/99f30bc974b6e808.
Report an issue: GitHub.