{"record":{"id":"7c23d2787df18160","repo":"hibernate/hibernate-orm","slug":"type-registration-was-corrupted-for-javaclass","errorCode":null,"errorMessage":"Type registration was corrupted for: \" + javaClass.getName()","messagePattern":"Type registration was corrupted for: \" \\+ javaClass\\.getName\\(\\)","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/JavaTypeRegistry.java","lineNumber":128,"sourceCode":"\t\treturn cached == null ? null : checkCached( javaClass, cached );\n\t}\n\n\tpublic <J> JavaType<J> resolveDescriptor(Class<? extends J> javaType, Supplier<JavaType<J>> creator) {\n\t\tfinal String javaTypeName = javaType.getTypeName();\n\t\tfinal var cached = descriptorsByTypeName.get( javaTypeName );\n\t\tif ( cached != null ) {\n\t\t\treturn checkCached( javaType, cached );\n\t\t}\n\t\telse {\n\t\t\tfinal var created = creator.get();\n\t\t\tfinal var cachedNew = descriptorsByTypeName.putIfAbsent( javaTypeName, created );\n\t\t\treturn cachedNew == null ? created : checkCached( javaType, cachedNew );\n\t\t}\n\t}\n\n\tprivate static <J> JavaType<J> checkCached(Class<? extends J> javaClass, JavaType<?> cached) {\n\t\tif ( cached.getJavaTypeClass() != canonicalize( javaClass ) ) {\n\t\t\tthrow new IllegalStateException( \"Type registration was corrupted for: \" + javaClass.getName() );\n\t\t}\n\t\t@SuppressWarnings(\"unchecked\") // safe, we just checked\n\t\tfinal var resolvedType = (JavaType<J>) cached;\n\t\treturn resolvedType;\n\t}\n\n\tprivate JavaType<?> resolveDescriptor(String javaTypeName, Supplier<? extends JavaType<?>> creator) {\n\t\tfinal var cached = descriptorsByTypeName.get( javaTypeName );\n\t\tif ( cached != null ) {\n\t\t\treturn cached;\n\t\t}\n\t\telse {\n\t\t\tfinal var created = creator.get();\n\t\t\tfinal var cachedNew = descriptorsByTypeName.putIfAbsent( javaTypeName, created );\n\t\t\treturn cachedNew == null ? created : cachedNew;\n\t\t}\n\t}\n","sourceCodeStart":110,"sourceCodeEnd":146,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/JavaTypeRegistry.java#L110-L146","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\npublic class MoneyJavaType extends AbstractClassJavaType<Money> {\n    public MoneyJavaType() { super(Object.class); } // wrong class -> corrupts registry key\n}\nregistry.addDescriptor(new MoneyJavaType());\nregistry.resolveDescriptor(Money.class, ...); // cached class Object != Money -> IllegalStateException\n\n// after\npublic class MoneyJavaType extends AbstractClassJavaType<Money> {\n    public MoneyJavaType() { super(Money.class); } // class matches registration key\n}","handlingStrategy":"validation","validationCode":"static void assertRegistrationConsistent(Class<?> key, JavaType<?> descriptor) {\n    if (descriptor.getJavaTypeClass() != key)\n        throw new IllegalStateException(\"descriptor \" + descriptor.getClass().getName()\n            + \" (for \" + descriptor.getJavaTypeClass().getName()\n            + \") cannot be registered under \" + key.getName());\n}","typeGuard":"static boolean registrationConsistent(Class<?> key, JavaType<?> descriptor) {\n    return descriptor.getJavaTypeClass() == key;\n}","tryCatchPattern":"try {\n    JavaType<Foo> jt = registry.resolveDescriptor(Foo.class, FooJavaType::new);\n} catch (IllegalStateException e) {\n    // duplicate/corrupt registration: find the contributor that registered a foreign\n    // descriptor under this name, remove one side, rebuild the SessionFactory\n}","preventionTips":["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"],"tags":["hibernate","orm","registry","type-registration","integrator"],"backgroundTag":"type-registration-error","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}