hibernate/hibernate-orm · error · SemanticException

Illegal format pattern '{}'

Error message

Illegal format pattern '{}'

What it means

The HQL format() function wraps its datetime pattern in an SqmFormat node whose constructor validates the pattern against a whitelist regex of supported DateTimeFormatter elements: quoted literals, punctuation separators, whitespace, and limited runs of G, y/Y, M, w, W, E, e, d, D, a, H/h/m/s, S, z/Z/x. Any pattern outside this subset throws SemanticException('Illegal format pattern ...') at query interpretation time.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/query/sqm/tree/spi/expression/SqmFormat.java:53

	// a AM/PM
	// H hour of day (0-23)
	// h clock hour of am/pm (1-12)
	// m minute of hour
	// s second of minute
	// S fraction of second
	// z time zone name e.g. PST
	// x zone offset e.g. +03, +0300, +03:00
	// Z zone offset e.g. +0300
	// see https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html
	private static final Pattern FORMAT = Pattern.compile( "('[^']+'|[:;/,.!@#$^&?~`|()\\[\\]{}<>\\-+*=]|\\s|G{1,2}|[yY]{1,4}|M{1,4}|w{1,2}|W|E{3,4}|e{1,2}|d{1,2}|D{1,3}|a|[Hhms]{1,2}|S{1,6}|[zZx]{1,3})*");

	public SqmFormat(
			String value,
			SqmBindableType<String> inherentType,
			NodeBuilder nodeBuilder) {
		super(value, inherentType, nodeBuilder);
		if (!FORMAT.matcher(value).matches()) {
			throw new SemanticException("Illegal format pattern '" + value + "'");
		}
	}

	@Override
	public @Nonnull SqmBindableType<String> getNodeType() {
		return castNonNull( super.getNodeType() );
	}

	@Override
	public @Nonnull String getLiteralValue() {
		return castNonNull( super.getLiteralValue() );
	}

	@Override
	public SqmFormat copy(SqmCopyContext context) {
		final SqmFormat existing = context.getCopy( this );
		if ( existing != null ) {
			return existing;

View on GitHub (pinned to fad1729dce)

Solutions

  1. Rewrite the pattern using only supported elements: u -> y, quarter -> month or precomputed quoted text.
  2. Wrap literal characters (including unsupported letters used as text) in single quotes: 'Q' or 'T'.
  3. Do complex formatting in Java after fetching, or fall back to a native query with the dialect's own format function when the pattern cannot be expressed.

Example fix

-- before
select format( e.ts as 'yyyy QQ u' ) from Event e  -- Q and u not supported

-- after
select format( e.ts as 'yyyy MM dd' ) from Event e
Defensive patterns

Strategy: validation

Validate before calling

private static final Pattern HQL_FORMAT = Pattern.compile(
    "('[^']+'|[:;/,.!@#$^&?~`|()\\[\\]{}<>\\-+*=]|\\s|G{1,2}|[yY]{1,4}|M{1,4}|w{1,2}|W|E{3,4}|e{1,2}|d{1,2}|D{1,3}|a|[Hhms]{1,2}|S{1,6}|[zZx]{1,3})*");

static String checkHqlFormat(String pattern) {
    if ( !HQL_FORMAT.matcher( pattern ).matches() ) {
        throw new IllegalArgumentException( "Pattern not supported by HQL format(): " + pattern );
    }
    return pattern;
}

Try / catch

try {
    return em.createQuery( hql ).getResultList();
} catch ( org.hibernate.query.SemanticException e ) {
    throw new IllegalArgumentException( "Bad format pattern: " + e.getMessage(), e );
}

Prevention

When it happens

Trigger: format( x.ts as '...' ) (or the criteria equivalent) with unsupported letters: u or uuuu years, Q/q quarters, L standalone month, c localized day, A milli-of-day, n nanos, or a letter repeated more times than allowed (e.g. six+ E's, seven+ S's); unbalanced quotes also fail to match.

Common situations: Copying a working java.time DateTimeFormatter pattern straight into HQL; quarter-based reporting patterns (QQ yyyy); strftime-style tokens (%Y-%m) or moment.js patterns pasted into the format string.

Related errors


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