hibernate/hibernate-orm · error · HibernateException

Could not convert '%s' to '%s' using '%s' to unwrap

Error message

Could not convert '%s' to '%s' using '%s' to unwrap

What it means

JavaTypeHelper.unknownUnwrap builds the HibernateException('Could not convert X to Y using Z to unwrap') thrown by JavaType.unwrap implementations when asked to convert a domain value to a class they do not support. unwrap runs at bind time to turn domain values into JDBC-friendly types (String, Long, java.sql types), so this error means the registered JavaType cannot produce what the JdbcType or binding context requested.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JavaTypeHelper.java:16

/*
 * SPDX-License-Identifier: Apache-2.0
 * Copyright Red Hat Inc. and Hibernate Authors
 */
package org.hibernate.type.descriptor.java;

import jakarta.annotation.Nullable;
import org.hibernate.HibernateException;
import org.hibernate.type.descriptor.java.spi.UnknownBasicJavaType;

/**
 * @author Steve Ebersole
 */
public class JavaTypeHelper {
	protected static <T extends JavaType<?>> HibernateException unknownUnwrap(Class<?> sourceType, Class<?> targetType, T jtd) {
		throw new HibernateException(
				"Could not convert '" + sourceType.getName()
						+ "' to '" + targetType.getName()
						+ "' using '" + jtd.getClass().getName() + "' to unwrap"
		);
	}

	protected static <T extends JavaType<?>> HibernateException unknownWrap(Class<?> valueType, Class<?> sourceType, T jtd) {
		throw new HibernateException(
				"Could not convert '" + valueType.getName()
						+ "' to '" + sourceType.getName()
						+ "' using '" + jtd.getClass().getName() + "' to wrap"
		);
	}

	public static boolean isTemporal(@Nullable JavaType<?> javaType) {
		return javaType != null && javaType.isTemporalType();
	}

View on GitHub (pinned to fad1729dce)

Solutions

  1. Read the message: it names sourceType, targetType, and the JavaType class — add the missing branch to that type's unwrap
  2. Fix the mapping: align @JavaType with the actual attribute type and @JdbcType/@JdbcTypeCode with the actual column
  3. Prefer extending AbstractJavaType or Hibernate's basic-type bases so unsupported paths degrade predictably
  4. If the type is an UnknownBasicJavaType, the JavaType was never resolved — register or annotate the type properly

Example fix

// before
@Override
public <X> X unwrap(Color value, Class<X> type, WrapperOptions options) {
    if (type == String.class) return (X) value.name();
    throw unknownUnwrap(type, Color.class, this); // any other target dies
}

// after
@Override
public <X> X unwrap(Color value, Class<X> type, WrapperOptions options) {
    if (value == null) return null;
    if (type == String.class) return (X) value.name();
    if (type == Integer.class) return (X) Integer.valueOf(value.getRGB());
    throw unknownUnwrap(type, Color.class, this);
}
Defensive patterns

Strategy: try-catch

Try / catch

try {
    session.persist(entity);
} catch (HibernateException ex) {
    if (ex.getMessage() != null && ex.getMessage().endsWith("to unwrap")) {
        // mapping bug: the named JavaType lacks the unwrap branch — fix the type/mapping
    }
}

Prevention

When it happens

Trigger: A custom JavaType/UserType whose unwrap handles only one branch (e.g. only String.class) being used with a JdbcType that requests another type; @JavaType/@JdbcType/@Type combinations that do not line up; parameter binding forcing a different unwrap target; an unresolved UnknownBasicJavaType being asked to unwrap.

Common situations: Registering a custom type but pairing it with the wrong column/JdbcType; Hibernate upgrades that added new unwrap targets; mixing @Type with @JdbcTypeCode on the same attribute; native query parameters with unexpected Java types.

Related errors


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