hibernate/hibernate-orm · error · IllegalStateException
Type registration was corrupted for: " + javaClass.getName()
Error message
Type registration was corrupted for: " + javaClass.getName()
What it means
After resolving a JavaType by class name, the registry verifies the cached descriptor's getJavaTypeClass() is exactly the (canonicalized) requested class. A mismatch means some registration stored a descriptor for class A under the map key of class B — a corrupted registration — and resolution aborts with IllegalStateException naming the class whose lookup failed.
Source
Thrown at hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/JavaTypeRegistry.java:128
return cached == null ? null : checkCached( javaClass, cached );
}
public <J> JavaType<J> resolveDescriptor(Class<? extends J> javaType, Supplier<JavaType<J>> creator) {
final String javaTypeName = javaType.getTypeName();
final var cached = descriptorsByTypeName.get( javaTypeName );
if ( cached != null ) {
return checkCached( javaType, cached );
}
else {
final var created = creator.get();
final var cachedNew = descriptorsByTypeName.putIfAbsent( javaTypeName, created );
return cachedNew == null ? created : checkCached( javaType, cachedNew );
}
}
private static <J> JavaType<J> checkCached(Class<? extends J> javaClass, JavaType<?> cached) {
if ( cached.getJavaTypeClass() != canonicalize( javaClass ) ) {
throw new IllegalStateException( "Type registration was corrupted for: " + javaClass.getName() );
}
@SuppressWarnings("unchecked") // safe, we just checked
final var resolvedType = (JavaType<J>) cached;
return resolvedType;
}
private JavaType<?> resolveDescriptor(String javaTypeName, Supplier<? extends JavaType<?>> creator) {
final var cached = descriptorsByTypeName.get( javaTypeName );
if ( cached != null ) {
return cached;
}
else {
final var created = creator.get();
final var cachedNew = descriptorsByTypeName.putIfAbsent( javaTypeName, created );
return cachedNew == null ? created : cachedNew;
}
}
View on GitHub (pinned to fad1729dce)
Solutions
- Make the descriptor's reported type consistent: getJavaTypeClass(), getJavaType() and the registration key must name the same class
- Audit all TypeContributors (META-INF/services entries) for duplicate registrations of the same class and keep exactly one
- Construct custom descriptors with the exact class they are registered for (super(TheClass.class))
- After hot reload, rebuild the SessionFactory/registry instead of reusing it across classloaders
Example fix
// before
public class MoneyJavaType extends AbstractClassJavaType<Money> {
public MoneyJavaType() { super(Object.class); } // wrong class -> corrupts registry key
}
registry.addDescriptor(new MoneyJavaType());
registry.resolveDescriptor(Money.class, ...); // cached class Object != Money -> IllegalStateException
// after
public class MoneyJavaType extends AbstractClassJavaType<Money> {
public MoneyJavaType() { super(Money.class); } // class matches registration key
} Defensive patterns
Strategy: validation
Validate before calling
static void assertRegistrationConsistent(Class<?> key, JavaType<?> descriptor) {
if (descriptor.getJavaTypeClass() != key)
throw new IllegalStateException("descriptor " + descriptor.getClass().getName()
+ " (for " + descriptor.getJavaTypeClass().getName()
+ ") cannot be registered under " + key.getName());
} Type guard
static boolean registrationConsistent(Class<?> key, JavaType<?> descriptor) {
return descriptor.getJavaTypeClass() == key;
} Try / catch
try {
JavaType<Foo> jt = registry.resolveDescriptor(Foo.class, FooJavaType::new);
} catch (IllegalStateException e) {
// duplicate/corrupt registration: find the contributor that registered a foreign
// descriptor under this name, remove one side, rebuild the SessionFactory
} Prevention
- One owner per registered type name across all integrators
- Assert getJavaTypeClass() equals the intended key in descriptor unit tests
- List and diff all TypeContributors (META-INF/services) on the classpath
- Rebuild the SessionFactory after hot reloads; never share registries across classloaders
When it happens
Trigger: Two integrators/TypeContributors registering different JavaTypes under the same type name; a custom JavaType whose getJavaType()/getTypeName() reports a different class than getJavaTypeClass(); re-registering over an existing entry via addDescriptor after another was cached; primitive-vs-wrapper mismatches under one name; classloader duplication after hot reload.
Common situations: Bundling multiple third-party type contributions (e.g. two JSON libraries) that claim the same class; version upgrades changing the descriptor for a JDK type; dev-tools hot reload producing same-named but different Class objects in the registry.
Related errors
- Illegal to add BasicJavaType with null Java type
- Named query definition is null
- Named query definition name is null: %s
- Duplicate named query '%s'
- Named native query definition object is null
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/7c23d2787df18160.
Report an issue: GitHub.