quarkusio/quarkus · error · BuildException
Type ${className} must be annotated with @Embeddable, becaus
Error message
Type ${className} must be annotated with @Embeddable, because it is used as an embeddable. This type is used in class ${declaringClass} for attribute ${attribute}. What it means
Quarkus validates at build time that any type used as an embeddable (via @Embedded, @EmbeddedId, or @ElementCollection on an embeddable type) is annotated with @Embeddable. If the target type lacks the annotation, a BuildException is thrown naming the type, declaring class, and attribute.
Source
Thrown at extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/JpaJandexScavenger.java:584
}
private void collectEmbeddedType(Set<DotName> embeddedTypes, ClassInfo declaringClass,
Declaration attribute, Type attributeType, boolean validate)
throws BuildException {
DotName className;
switch (attributeType.kind()) {
case CLASS:
className = attributeType.asClassType().name();
break;
case PARAMETERIZED_TYPE:
className = attributeType.name();
break;
default:
// do nothing
return;
}
if (validate && !index.getClassByName(className).hasAnnotation(ClassNames.EMBEDDABLE)) {
throw new BuildException(
"Type " + className + " must be annotated with @Embeddable, because it is used as an embeddable."
+ " This type is used in class " + declaringClass
+ " for attribute " + attribute + ".");
}
embeddedTypes.add(attributeType.name());
}
private void collectElementCollectionTypes(Set<DotName> embeddedTypes, ClassInfo declaringClass,
Declaration attribute, Type attributeType)
throws BuildException {
switch (attributeType.kind()) {
case CLASS:
// Raw collection type, nothing we can do
break;
case PARAMETERIZED_TYPE:
embeddedTypes.add(attributeType.name());
var typeArguments = attributeType.asParameterizedType().arguments();
for (Type typeArgument : typeArguments) {View on GitHub (pinned to e1c734241f)
Solutions
- Annotate the referenced type with jakarta.persistence.Embeddable
- If the type should be its own entity, change the field mapping (remove @Embedded, use @OneToOne etc.)
- If the type is in another module, ensure it is part of the application and indexed by Quarkus
- Verify the attribute name and declaring class in the message to find the exact field to fix
Example fix
// before
public class Address { private String street; }
@Entity public class Person { @Embedded Address address; }
// after
@Embeddable
public class Address { private String street; }
@Entity public class Person { @Embedded Address address; } Defensive patterns
Strategy: validation
Validate before calling
// Check before mapping an @Embedded field
static void requireEmbeddable(Class<?> type) {
if (!type.isAnnotationPresent(jakarta.persistence.Embeddable.class))
throw new IllegalArgumentException(type + " must be @Embeddable");
} Type guard
static boolean isEmbeddable(Class<?> t) {
return t.isAnnotationPresent(jakarta.persistence.Embeddable.class);
} Prevention
- Always add @Embeddable to types used in @Embedded/@EmbeddedId/@ElementCollection fields
- Add an ArchUnit/reflection test asserting embeddable usage is annotated
- Distinguish entity vs embeddable types early in code review
When it happens
Trigger: collectEmbeddedType is invoked from enlistEmbeddedsAndElementCollections or collectElementCollectionTypes when an entity field annotated @Embedded/@EmbeddedId/@ElementCollection points to a type without @Embeddable in the Jandex index.
Common situations: Using an entity or plain POJO as an @Embedded field; forgetting @Embeddable when introducing an embeddable; the embeddable class present but not indexed; switching a field type from entity to embeddable without adding the annotation.
Related errors
- Cannot find ORM mapping file '${mappingFileName}' in the cla
- Missing attribute '${nodeName}.class'
- Annotation ${dotName} was not expected on a target of kind $
- Multiple ${AdditionalPersistenceUnitBuildItem} for persisten
- Hibernate %s persistence unit '%s' cannot be created for the
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/21755d78af3139b4.
Report an issue: GitHub.