quarkusio/quarkus · error · java.lang.IllegalArgumentException
The qualifier ${aClass} was used repeatedly but it is not an
Error message
The qualifier ${aClass} was used repeatedly but it is not annotated with @java.lang.annotation.Repeatable What it means
When building qualifier sets, Qualifiers counts how often each qualifier annotation appears. If the same qualifier class is supplied more than once and that annotation is not meta-annotated with @Repeatable, the combination is invalid per CDI semantics and IllegalArgumentException is thrown.
Source
Thrown at independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/Qualifiers.java:79
Map<Class<? extends Annotation>, Integer> timesQualifierWasSeen = new HashMap<>();
for (Annotation qualifier : qualifiers) {
verifyQualifier(qualifier.annotationType());
timesQualifierWasSeen.compute(qualifier.annotationType(), TimesSeenBiFunction.INSTANCE);
}
checkQualifiersForDuplicates(timesQualifierWasSeen);
}
}
// in various cases, specification requires to check qualifiers for duplicates and throw IAE
private static void checkQualifiersForDuplicates(Map<Class<? extends Annotation>, Integer> timesQualifierSeen) {
for (Entry<Class<? extends Annotation>, Integer> entry : timesQualifierSeen.entrySet()) {
checkQualifiersForDuplicates(entry.getKey(), entry.getValue());
}
}
private static void checkQualifiersForDuplicates(Class<? extends Annotation> aClass, Integer times) {
if (times > 1 && (aClass.getAnnotation(Repeatable.class) == null)) {
throw new IllegalArgumentException("The qualifier " + aClass + " was used repeatedly " +
"but it is not annotated with @java.lang.annotation.Repeatable");
}
}
boolean hasQualifiers(Set<Annotation> beanQualifiers, Annotation... requiredQualifiers) {
for (Annotation qualifier : requiredQualifiers) {
if (!hasQualifier(beanQualifiers, qualifier)) {
return false;
}
}
return true;
}
boolean hasQualifier(Iterable<Annotation> qualifiers, Annotation requiredQualifier) {
Class<? extends Annotation> requiredQualifierClass = requiredQualifier.annotationType();
Method[] members = requiredQualifierClass.getDeclaredMethods();
View on GitHub (pinned to e1c734241f)
Solutions
- Pass each qualifier only once — duplicates are redundant since qualifiers match conjunctively.
- If repetition is intentional, add @Repeatable to the qualifier annotation (with a container annotation) — though the CDI runtime here rejects duplicates regardless.
- Deduplicate the qualifier array before calling select()/instance() (e.g. new LinkedHashSet<>(Arrays.asList(qualifiers))).
- Use distinct qualifiers (e.g. @Named values) if you need multiple constraints.
Example fix
// before Instance<Foo> i = Arc.container().select(Foo.class, new Qual1L(), new Qual1L()); // after Instance<Foo> i = Arc.container().select(Foo.class, new Qual1L());
Defensive patterns
Strategy: validation
Validate before calling
Set<Class<? extends Annotation>> seen = new LinkedHashSet<>();
for (Annotation q : qualifiers) {
if (!seen.add(q.annotationType())) {
throw new IllegalArgumentException("Duplicate qualifier " + q.annotationType() + " (not repeatable)");
}
} Type guard
boolean allDistinct(Annotation... qs) {
return Arrays.stream(qs).map(Annotation::annotationType).distinct().count() == qs.length;
} Try / catch
try {
return Arc.container().select(type, qualifiers);
} catch (IllegalArgumentException e) {
if (e.getMessage().contains("not annotated with @java.lang.annotation.Repeatable")) {
return Arc.container().select(type, Arrays.stream(qualifiers).distinct().toArray(Annotation[]::new));
}
throw e;
} Prevention
- Deduplicate qualifier arrays before any CDI lookup
- Never append the same qualifier twice when collecting constraints programmatically
- Treat qualifiers as conjunctive: one instance per type is enough
When it happens
Trigger: Calling Arc.container().instance(Foo.class, new Literal(), new Literal()) or any select()/qualifier API passing the same non-repeatable qualifier annotation instance/class twice.
Common situations: Programmatic lookup code that accumulates qualifiers in a loop and appends duplicates; refactored code merging qualifier arrays; misunderstanding @Repeatable requirements when multiple identical qualifiers are meant to AND together.
Related errors
- Qualifier annotation '${qualifierClass}' contains a member '
- A context class must be a public non-abstract top-level or s
- @Named without value may not be used on method parameter:
- Error checking value of member method ${methodName} on ${dec
- Annotation is not a registered qualifier: ${annotationType}
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/297152f2aa402654.
Report an issue: GitHub.