hibernate/hibernate-orm · error · IllegalArgumentException

Illegal XML content:

Error message

Illegal XML content: 

What it means

XmlHelper.unescape understands only the five named XML entities (< & ' > "). When Hibernate reads an XML-mapped aggregate column, any other ampersand sequence in the text content fails entity matching and throws IllegalArgumentException with the offending content. This includes numeric character references, HTML-only entities, and bare '&' characters.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/XmlHelper.java:116

							case 'g':
								if ( string.charAt( i + 2 ) == 't' && string.charAt( i + 3 ) == ';' ) {
									sb.append( '>' );
									i += 3;
								}
								break OUTER;
							case 'q':
								if ( i + 5 < end
									&& string.charAt( i + 2 ) == 'u'
									&& string.charAt( i + 3 ) == 'o'
									&& string.charAt( i + 4 ) == 't'
									&& string.charAt( i + 5 ) == ';' ) {
									sb.append( '"' );
									i += 5;
								}
								break OUTER;
						}
					}
					throw new IllegalArgumentException( "Illegal XML content: " + string.substring( start, end ) );
				default:
					sb.append( c );
					break;
			}
		}
		return sb.toString();
	}

	private static Object fromString(
			EmbeddableMappingType embeddableMappingType,
			String string,
			boolean returnEmbeddable,
			WrapperOptions options,
			int selectableIndex,
			int start,
			int end) throws SQLException {
		final JdbcMapping jdbcMapping = embeddableMappingType.getJdbcValueSelectable( selectableIndex )
				.getJdbcMapping();

View on GitHub (pinned to fad1729dce)

Solutions

  1. Repair the stored data: replace unsupported entities with the literal character and escape stray '&' as '&amp;'.
  2. Write XML aggregate values only through Hibernate so escaping stays consistent.
  3. Add an ingest-time sanitizer if external writers must touch the column.
  4. Audit the column for suspicious '&' rows before switching reads to Hibernate.

Example fix

-- PostgreSQL audit + fix
-- before: content holds '&nbsp;' or '&#65;'
UPDATE t SET doc = REPLACE(doc, '&nbsp;', ' ') WHERE doc LIKE '%&nbsp;%';
SELECT id FROM t WHERE doc ~ '&(?!amp;|lt;|gt;|apos;|quot;)' AND doc LIKE '%<e>%';
Defensive patterns

Strategy: try-catch

Validate before calling

static boolean hasOnlySupportedEntities(String content) {
    return content == null || !content.matches("(?s).*&(?!(amp|lt|gt|apos|quot);).*");
}

Try / catch

try {
    return session.find(Person.class, id); // materializes the XML aggregate
} catch (IllegalArgumentException ex) {
    if (ex.getMessage() != null && ex.getMessage().startsWith("Illegal XML content")) {
        quarantine(id, ex.getMessage());   // log PK + offending content, schedule repair
        return null;
    }
    throw ex;
}

Prevention

When it happens

Trigger: Reading an XML aggregate (XmlJdbcType / SqlTypes.SQLXML mapping) whose stored text contains an ampersand sequence outside the five supported entities, e.g. '&#65;', '&nbsp;', '&#x41;', or an unescaped '&' followed by a character other than l/a/g/q; also an entity truncated at the end of the content region.

Common situations: Rows written by another application, ETL job, or script that used broader XML/HTML escaping; hand-edited column values; data migrated from standard XML documents into a Hibernate XML aggregate column.

Related errors


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