quarkusio/quarkus · error · DefinitionException
Qualifier annotation '${qualifierClass}' contains a member '
Error message
Qualifier annotation '${qualifierClass}' contains a member '${member}' with ${problem}-valued return type. All such members have to be annotated with @jakarta.enterprise.util.Nonbinding What it means
CDI qualifiers may only have binding members of types that support equality: primitives, String, Class, enum, annotation, and arrays thereof. During BeanDeployment validation, Arc inspects each qualifier member's return type; if it's some other class type (e.g. an arbitrary POJO) — even potentially an annotation it cannot resolve — it throws DefinitionException requiring @Nonbinding on such members.
Source
Thrown at independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanDeployment.java:911
* @param qualifierClass class info of the qualifier
* @param nonbindingMembers collection of members we consider {@code @Nonbinding} for synthetic qualifier, null otherwise
*/
private void validateQualifier(ClassInfo qualifierClass, Set<String> nonbindingMembers) {
for (MethodInfo mi : qualifierClass.methods()) {
Type returnType = mi.returnType();
if ((nonbindingMembers != null && !nonbindingMembers.contains(mi.name()))
|| (nonbindingMembers == null && mi.annotation(DotNames.NONBINDING) == null)) {
String problem = null;
if (returnType.kind().equals(Type.Kind.ARRAY)) {
problem = "array";
} else if (returnType.kind().equals(Type.Kind.CLASS)) {
ClassInfo typeClassInfo = beanArchiveImmutableIndex.getClassByName(returnType.asClassType().name());
if (typeClassInfo != null && typeClassInfo.isAnnotation()) {
problem = "annotation";
}
}
if (problem != null) {
throw new DefinitionException("Qualifier annotation '" + qualifierClass + "' contains a member '"
+ mi.name()
+ "' with " + problem
+ "-valued return type. All such members have to be annotated with @jakarta.enterprise.util.Nonbinding");
}
}
}
}
private Map<DotName, ClassInfo> findContainerAnnotations(Map<DotName, ClassInfo> annotations) {
Map<DotName, ClassInfo> containerAnnotations = new HashMap<>();
for (ClassInfo annotation : annotations.values()) {
AnnotationInstance repeatableMetaAnnotation = annotation.declaredAnnotation(DotNames.REPEATABLE);
if (repeatableMetaAnnotation != null) {
DotName containerAnnotationName = repeatableMetaAnnotation.value().asClass().name();
ClassInfo containerClass = getClassByName(getBeanArchiveIndex(), containerAnnotationName);
containerAnnotations.put(containerAnnotationName, containerClass);
}
}View on GitHub (pinned to e1c734241f)
Solutions
- Add @jakarta.enterprise.util.Nonbinding to the offending member
- Change the member type to a binding-safe type (String, Class, enum, primitive, nested annotation, or arrays of these)
- Remove the member if it isn't needed for matching
- If it must carry complex data, encode it as a String and parse it where used
Example fix
// before
@Qualifier @Retention(RUNTIME) @interface Fast { MyConfig value(); }
// after
@Qualifier @Retention(RUNTIME) @interface Fast { @Nonbinding MyConfig value(); } Defensive patterns
Strategy: validation
Validate before calling
for (MethodInfo m : qualifierClassInfo.methods()) {
if (m.isStatic() || m.isSynthetic()) continue;
Type r = m.returnType();
boolean bindingSafe = r.kind() == Type.Kind.PRIMITIVE || isLegalArrayType(r) || isStringClassEnumAnnotation(r);
if (!bindingSafe && !m.hasDeclaredAnnotation(DotNames.NONBINDING)) throw new DefinitionException(m + " must be @Nonbinding");
} Try / catch
try { deployment.init(); }
catch (DefinitionException e) { log.error(e.getMessage() + " — add @Nonbinding or change the member type"); throw e; } Prevention
- Restrict qualifier members to primitive/String/Class/enum/annotation types
- Always annotate complex members @Nonbinding
- Review qualifiers ported from Spring/Guice for type compatibility
When it happens
Trigger: Declaring a qualifier annotation with a member whose return type is a class type other than the allowed ones (or not @Nonbinding), e.g. @MyQualifier Foo foo(); where Foo is an interface/class that's not an annotation or unsupported type.
Common situations: Custom qualifiers with Object/POJO/list-typed members; qualifiers copied from Spring/Guice code (@Guice allows arbitrary types); missing @Nonbinding after refactoring a member type to a complex type.
Related errors
- Stereotype must not declare @Named with a non-empty value: $
- Different scopes defined for: ${target}; scopes: ${scopes}
- @Named without value may not be used on method parameter:
- The qualifier ${aClass} was used repeatedly but it is not an
- Error checking value of member method ${methodName} on ${dec
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/a38a92582bd36b17.
Report an issue: GitHub.