hibernate/hibernate-orm · error · IllegalArgumentException

Can't cast expression to unknown type: {}

Error message

Can't cast expression to unknown type: {}

What it means

AbstractSqmExpression.as(Class) implements the JPA cast by looking up a registered BasicType for the target Java type (nodeBuilder().getTypeConfiguration().getBasicTypeForJavaType(type)). If the type registry has no basic type for that class — custom value objects, entity classes, Object, unregistered wrapper types — the lookup returns null and Hibernate throws IllegalArgumentException 'Can't cast expression to unknown type'.

Source

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

	}

	protected void internalApplyInferableType(@Nullable SqmBindableType<?> newType) {
//		SqmTreeCreationLogger.LOGGER.tracef(
//				"Applying inferable type to SqmExpression [%s]: %s -> %s",
//				this,
//				getExpressible(),
//				newType
//		);

		setExpressibleType( highestPrecedenceType2( newType, getExpressible() ) );
	}

	@Nonnull
	@Override
	public <X> SqmExpression<X> as(@Nonnull Class<X> type) {
		final BasicType<X> basicTypeForJavaType = nodeBuilder().getTypeConfiguration().getBasicTypeForJavaType( type );
		if ( basicTypeForJavaType == null ) {
			throw new IllegalArgumentException( "Can't cast expression to unknown type: " + type.getCanonicalName() );
		}
		return new AsWrapperSqmExpression<>( basicTypeForJavaType, this );
	}

	@Nonnull
	@Override
	public SqmPredicate isNull() {
		return nodeBuilder().isNull( this );
	}

	@Nonnull
	@Override
	public SqmPredicate isNotNull() {
		return nodeBuilder().isNotNull( this );
	}

	@Nonnull
	@Override

View on GitHub (pinned to fad1729dce)

Solutions

  1. Cast only to Java types Hibernate maps as basic (String, Integer, Long, BigDecimal, LocalDate, ...).
  2. For entity narrowing use treatAs()/HQL TREAT instead of as(); for pure Java-side casts, cast the value after the query returns.
  3. Register a type for the custom class: an @JavaTypeRegistration/@Type registration, an AttributeConverter, or a TypeContributor — after that .as(CustomClass.class) resolves.
  4. Bind the value as a typed parameter (parameter binding uses converters) instead of casting an expression.

Example fix

// before
Expression<String> raw = root.get("amount");
raw.as(MonetaryAmount.class); // no BasicType for MonetaryAmount -> IllegalArgumentException
// after
// 1) register a converter for MonetaryAmount (e.g. @Converter auto-applied), then:
Expression<MonetaryAmount> amt = cb.toLong(...) /* or use the converted path directly */;
// 2) or avoid the cast entirely:
List<MonetaryAmount> amounts = rows.stream().map(r -> r.getAmount()).toList();
Defensive patterns

Strategy: validation

Validate before calling

static boolean castSupported(SessionFactory sf, Class<?> javaType) {
    return ((SessionFactoryImplementor) sf).getTypeConfiguration()
            .getBasicTypeForJavaType(javaType) != null; // null -> as() would throw
}

Type guard

static boolean castableToBasic(Class<?> target) {
    // quick allow-list of common basic types before calling Expression.as
    return target == String.class || Number.class.isAssignableFrom(target)
            || target == Boolean.class || java.time.temporal.Temporal.class.isAssignableFrom(target);
}

Try / catch

try {
    casted = expression.as(targetClass);
} catch (IllegalArgumentException e) {
    if (e.getMessage() != null && e.getMessage().startsWith("Can't cast expression")) {
        casted = null; // bind as typed parameter or register a converter instead
    } else throw e;
}

Prevention

When it happens

Trigger: criteriaExpr.as(MonetaryAmount.class) or any .as(SomeCustomClass.class) where no BasicType/AttributeConverter/@JavaTypeRegistration exists for the class; attempting .as(OtherEntity.class) (entity narrowing is not a cast); casting to JDK types Hibernate does not map by default.

Common situations: Porting criteria code from providers where as() was a silent Java-side cast; using .as() on custom value types after upgrading to Hibernate 6, which made as() produce a real SQL cast requiring a resolved type; casting to DTO or entity classes.

Related errors


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