{"record":{"id":"6c82ce52f319b9d7","repo":"hibernate/hibernate-orm","slug":"multiple-entities-s-s-named-the-same-interfac","errorCode":null,"errorMessage":"Multiple entities [%s, %s] named the same interface [%s] as their proxy which is not supported","messagePattern":"Multiple entities \\[(.+?), (.+?)\\] named the same interface \\[(.+?)\\] as their proxy which is not supported","errorType":"exception","errorClass":"HibernateException","httpStatus":null,"severity":"critical","filePath":"hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/MappingMetamodelImpl.java","lineNumber":267,"sourceCode":"\t\t\tfinal String className = model.getClassName();\n\t\t\tif ( className != null && !className.equals( entityName ) ) {\n\t\t\t\t// But only if the class name is not registered already,\n\t\t\t\t// as we can have the same class mapped to multiple entity names\n\t\t\t\tentityPersisterMap.putIfAbsent( className, entityPersister );\n\t\t\t}\n\n\t\t\tfinal var concreteProxyClass = entityPersister.getConcreteProxyClass();\n\t\t\tfinal var mappedClass = entityPersister.getMappedClass();\n\t\t\tif ( concreteProxyClass != null\n\t\t\t\t\t&& concreteProxyClass.isInterface()\n\t\t\t\t\t// we exclude Map-based proxy interfaces here because that should indicate MAP entity mode\n\t\t\t\t\t&& !Map.class.isAssignableFrom( concreteProxyClass )\n\t\t\t\t\t&& mappedClass != concreteProxyClass ) {\n\t\t\t\tfinal String existing =\n\t\t\t\t\t\tentityProxyInterfaceMap.put( concreteProxyClass,\n\t\t\t\t\t\t\t\tentityPersister.getEntityName() );\n\t\t\t\tif ( existing != null ) {\n\t\t\t\t\tthrow new HibernateException(\n\t\t\t\t\t\t\tString.format(\n\t\t\t\t\t\t\t\t\tLocale.ENGLISH,\n\t\t\t\t\t\t\t\t\t\"Multiple entities [%s, %s] named the same interface [%s] as their proxy which is not supported\",\n\t\t\t\t\t\t\t\t\texisting,\n\t\t\t\t\t\t\t\t\tentityPersister.getEntityName(),\n\t\t\t\t\t\t\t\t\tconcreteProxyClass.getName()\n\t\t\t\t\t\t\t)\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void processBootCollections(\n\t\t\tjava.util.Collection<Collection> collectionBindings,\n\t\t\tCacheImplementor cacheImplementor,\n\t\t\tPersisterFactory persisterFactory,\n\t\t\tRuntimeModelCreationContext modelCreationContext) {","sourceCodeStart":249,"sourceCodeEnd":285,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/MappingMetamodelImpl.java#L249-L285","documentation":"During MappingMetamodel bootstrap Hibernate indexes each entity's proxy interface in entityProxyInterfaceMap (used to resolve entities by class later). If two different entities register the SAME interface as their concrete proxy class, the inverse lookup becomes ambiguous and HibernateException is thrown at SessionFactory creation.","triggerScenarios":"Two entities each declare @Proxy(proxyClass = SameInterface.class) (or otherwise end up with the same interface as concreteProxyClass while mappedClass differs, excluding Map-based MAP entity mode); e.g. an interface 'Auditable' used as proxy on both Article and Comment.","commonSituations":"Copy-pasted @Proxy annotations across entities implementing a shared domain interface; introducing @Proxy on a second implementation of an existing proxy interface; consolidating entities that accidentally share a marker interface as proxy.","solutions":["Give each entity its own distinct proxy interface and reference it in @Proxy(proxyClass=...)","Drop @Proxy on the entity to fall back to default class-based proxies","If the shared type must be the polymorphic handle, make it the @Entity hierarchy root instead of a proxy interface on siblings"],"exampleFix":"// before\npublic interface Identifiable { Long getId(); }\n@Entity @Proxy(proxyClass = Identifiable.class) public class Article implements Identifiable {...}\n@Entity @Proxy(proxyClass = Identifiable.class) public class Comment implements Identifiable {...} // duplicate -> HibernateException\n\n// after\n@Entity public class Article implements Identifiable {...}\n@Entity public class Comment implements Identifiable {...} // no @Proxy, default proxies","handlingStrategy":"try-catch","validationCode":"// Static pre-flight: detect duplicate proxy interfaces across mapped entities before EMF build\nMap<Class<?>, String> byInterface = new HashMap<>();\nfor (Class<?> c : scannedEntityClasses) {\n  org.hibernate.annotations.Proxy proxy = c.getAnnotation(org.hibernate.annotations.Proxy.class);\n  if (proxy != null && proxy.proxyClass().isInterface()) {\n    String prev = byInterface.put(proxy.proxyClass(), c.getName());\n    if (prev != null) {\n      throw new IllegalStateException(\"Proxy interface \" + proxy.proxyClass()\n          + \" used by both \" + prev + \" and \" + c.getName());\n    }\n  }\n}","typeGuard":null,"tryCatchPattern":"try {\n  sessionFactory = new Configuration().addAnnotatedClass(...).buildSessionFactory();\n} catch (HibernateException e) {\n  if (e.getMessage() != null && e.getMessage().contains(\"named the same interface\")) {\n    // two entities share one @Proxy interface — give each entity its own proxy interface or drop @Proxy\n    throw new IllegalStateException(\"Duplicate proxy interface between entities: \" + e.getMessage(), e);\n  }\n  throw e;\n}","preventionTips":["Use one dedicated proxy interface per entity, or skip @Proxy and rely on default class proxies","Add the duplicate-detection pre-flight above to startup so the failure names both classes before Hibernate does","Review @Proxy usage whenever adding a new entity implementing an existing proxy interface"],"tags":["hibernate","proxy","entity-mapping","bootstrap"],"backgroundTag":"duplicate-proxy-interface","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}