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

  1. Make the descriptor's reported type consistent: getJavaTypeClass(), getJavaType() and the registration key must name the same class
  2. Audit all TypeContributors (META-INF/services entries) for duplicate registrations of the same class and keep exactly one
  3. Construct custom descriptors with the exact class they are registered for (super(TheClass.class))
  4. 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

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


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