hibernate/hibernate-orm · error · IllegalArgumentException

Invalid temporal field [{}]

Error message

Invalid temporal field [{}]

What it means

The criteria overload extract(TemporalField, Expression) maps the field to a TemporalUnit via a switch over field.toString(); only a fixed set of names is recognized (year, quarter, month, week, day, hour, minute, second, date, time per the surrounding switch). Any other string form — a custom enum constant, a typo, or fields Hibernate's switch doesn't cover — hits default and throws IllegalArgumentException.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmCriteriaNodeBuilder.java:3743

				temporalUnit = TemporalUnit.HOUR;
				break;
			case "minute":
				temporalUnit = TemporalUnit.MINUTE;
				break;
			case "second":
				temporalUnit = TemporalUnit.SECOND;
				resultType = Double.class;
				break;
			case "date":
				temporalUnit = TemporalUnit.DATE;
				resultType = LocalDate.class;
				break;
			case "time":
				temporalUnit = TemporalUnit.TIME;
				resultType = LocalTime.class;
				break;
			default:
				throw new IllegalArgumentException( "Invalid temporal field [" + field + "]" );
		}
		//noinspection unchecked
		return extract( temporal, temporalUnit, (Class<N>) resultType );
	}

	private <T> SqmFunction<T> extract(
			Expression<? extends TemporalAccessor> datetime,
			TemporalUnit temporalUnit,
			Class<T> type) {
		return getFunctionDescriptor( "extract" ).generateSqmExpression(
				asList(
						new SqmExtractUnit<>(
								temporalUnit,
								getTypeConfiguration().standardBasicTypeForJavaType( type ),
								this
						),
						(SqmTypedNode<?>) datetime
				),

View on GitHub (pinned to fad1729dce)

Solutions

  1. Use the unit-based overload: cb.extract(TemporalUnit.YEAR, ts) with org.hibernate.query.TemporalUnit, which has no string switch.
  2. If you keep the TemporalField overload, make your enum's toString() return one of the recognized names exactly (lowercase: year, quarter, month, week, day, hour, minute, second, date, time).
  3. Map your public API field names to the supported names before calling extract.

Example fix

// before
enum F { ISO_YEAR } // toString() = "ISO_YEAR"
cb.extract(F.ISO_YEAR, ts); // Invalid temporal field [ISO_YEAR]

// after
import org.hibernate.query.TemporalUnit;
cb.extract(TemporalUnit.YEAR, ts);
Defensive patterns

Strategy: validation

Validate before calling

private static final Set<String> SUPPORTED = Set.of(
        "year", "quarter", "month", "week", "day",
        "hour", "minute", "second", "date", "time");

boolean supported(String field) { return SUPPORTED.contains(field.toLowerCase(Locale.ROOT)); }
if (!supported(field.toString())) throw new IllegalArgumentException("Unsupported temporal field: " + field);

Type guard

static boolean extractFieldSupported(TemporalField f) {
    return Set.of("year","quarter","month","week","day","hour","minute","second","date","time")
            .contains(f.toString());
}

Try / catch

try {
    e = cb.extract(field, ts);
} catch (IllegalArgumentException ex) {
    if (ex.getMessage().contains("Invalid temporal field")) e = cb.extract(TemporalUnit.YEAR, ts);
    else throw ex;
}

Prevention

When it happens

Trigger: cb.extract(TemporalField.YEAR, ts) where TemporalField is a user-defined enum whose constant prints differently (e.g. 'Year' or 'YEAR_OF_ERA'); passing java.time.temporal.ChronoField constants not handled by the switch; typos in string-based field selection.

Common situations: Defining your own TemporalField-like enum for a query DSL and forwarding its constants; assuming every java.time.temporal.ChronoField is supported; upgrading Hibernate versions where the accepted-field switch changed.

Related errors


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