{"record":{"id":"a38a92582bd36b17","repo":"quarkusio/quarkus","slug":"qualifier-annotation-qualifierclass-contains","errorCode":null,"errorMessage":"Qualifier annotation '${qualifierClass}' contains a member '${member}' with ${problem}-valued return type. All such members have to be annotated with @jakarta.enterprise.util.Nonbinding","messagePattern":"Qualifier annotation '(.+?)' contains a member '(.+?)' with (.+?)-valued return type\\. All such members have to be annotated with @jakarta\\.enterprise\\.util\\.Nonbinding","errorType":"exception","errorClass":"DefinitionException","httpStatus":null,"severity":"error","filePath":"independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanDeployment.java","lineNumber":911,"sourceCode":"     * @param qualifierClass class info of the qualifier\n     * @param nonbindingMembers collection of members we consider {@code @Nonbinding} for synthetic qualifier, null otherwise\n     */\n    private void validateQualifier(ClassInfo qualifierClass, Set<String> nonbindingMembers) {\n        for (MethodInfo mi : qualifierClass.methods()) {\n            Type returnType = mi.returnType();\n            if ((nonbindingMembers != null && !nonbindingMembers.contains(mi.name()))\n                    || (nonbindingMembers == null && mi.annotation(DotNames.NONBINDING) == null)) {\n                String problem = null;\n                if (returnType.kind().equals(Type.Kind.ARRAY)) {\n                    problem = \"array\";\n                } else if (returnType.kind().equals(Type.Kind.CLASS)) {\n                    ClassInfo typeClassInfo = beanArchiveImmutableIndex.getClassByName(returnType.asClassType().name());\n                    if (typeClassInfo != null && typeClassInfo.isAnnotation()) {\n                        problem = \"annotation\";\n                    }\n                }\n                if (problem != null) {\n                    throw new DefinitionException(\"Qualifier annotation '\" + qualifierClass + \"' contains a member '\"\n                            + mi.name()\n                            + \"' with \" + problem\n                            + \"-valued return type. All such members have to be annotated with @jakarta.enterprise.util.Nonbinding\");\n                }\n            }\n        }\n    }\n\n    private Map<DotName, ClassInfo> findContainerAnnotations(Map<DotName, ClassInfo> annotations) {\n        Map<DotName, ClassInfo> containerAnnotations = new HashMap<>();\n        for (ClassInfo annotation : annotations.values()) {\n            AnnotationInstance repeatableMetaAnnotation = annotation.declaredAnnotation(DotNames.REPEATABLE);\n            if (repeatableMetaAnnotation != null) {\n                DotName containerAnnotationName = repeatableMetaAnnotation.value().asClass().name();\n                ClassInfo containerClass = getClassByName(getBeanArchiveIndex(), containerAnnotationName);\n                containerAnnotations.put(containerAnnotationName, containerClass);\n            }\n        }","sourceCodeStart":893,"sourceCodeEnd":929,"githubUrl":"https://github.com/quarkusio/quarkus/blob/e1c734241f34c7919086ceb4c9262b4a58f6de44/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanDeployment.java#L893-L929","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\n@Qualifier @Retention(RUNTIME) @interface Fast { MyConfig value(); }\n// after\n@Qualifier @Retention(RUNTIME) @interface Fast { @Nonbinding MyConfig value(); }","handlingStrategy":"validation","validationCode":"for (MethodInfo m : qualifierClassInfo.methods()) {\n    if (m.isStatic() || m.isSynthetic()) continue;\n    Type r = m.returnType();\n    boolean bindingSafe = r.kind() == Type.Kind.PRIMITIVE || isLegalArrayType(r) || isStringClassEnumAnnotation(r);\n    if (!bindingSafe && !m.hasDeclaredAnnotation(DotNames.NONBINDING)) throw new DefinitionException(m + \" must be @Nonbinding\");\n}","typeGuard":null,"tryCatchPattern":"try { deployment.init(); }\ncatch (DefinitionException e) { log.error(e.getMessage() + \" — add @Nonbinding or change the member type\"); throw e; }","preventionTips":["Restrict qualifier members to primitive/String/Class/enum/annotation types","Always annotate complex members @Nonbinding","Review qualifiers ported from Spring/Guice for type compatibility"],"tags":["cdi","qualifier","nonbinding","definition-error","arc"],"backgroundTag":"qualifier-member-not-nonbinding","analyzedSha":"e1c734241f34c7919086ceb4c9262b4a58f6de44","analyzedAt":"2026-09-05T17:01:29.979Z","contentChangedAt":"2026-09-05T17:01:29.979Z","schemaVersion":2},"datasetVersion":"2026-09-12T22:17:10.623Z"}