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 unchecked

View on GitHub (pinned to fad1729dce)

Solutions

  1. Rewrite column data into the <e>...</e> aggregate format (or null it) with a migration script.
  2. Write values through Hibernate once so the column converges on the correct format.
  3. 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

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


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