quarkusio/quarkus · error · DefinitionException

Multiple instances of non-repeatable interceptor binding ann

Error message

Multiple instances of non-repeatable interceptor binding annotation 

What it means

ArC throws this DefinitionException when a class (the interceptor class or a bean class) declares the same non-repeatable interceptor binding annotation more than once — directly and/or via inheritance — with different member values. Interceptor bindings must be unambiguous: for a non-repeatable binding type, all occurrences must resolve to identical member values (defaults included). Detected at build time.

Source

Thrown at independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/Interceptors.java:126

                // don't validate @Repeatable interceptor bindings, repeatability is their entire point
                continue;
            }

            List<AnnotationValue> seenValues = seenBindings.get(name);
            if (seenValues != null) {
                // interceptor binding of the same type already seen
                // all annotation members (except nonbinding) must have equal values
                ClassInfo declaration = beanDeployment.getInterceptorBinding(name);
                Set<String> nonBindingMembers = beanDeployment.getInterceptorNonbindingMembers(name);

                for (AnnotationValue value : seenValues) {
                    if (declaration.method(value.name()).hasDeclaredAnnotation(DotNames.NONBINDING)
                            || nonBindingMembers.contains(value.name())) {
                        continue;
                    }

                    if (!value.equals(binding.valueWithDefault(index, value.name()))) {
                        throw new DefinitionException("Multiple instances of non-repeatable interceptor binding annotation "
                                + name + " with different member values on class " + targetClass);
                    }
                }
            } else {
                // interceptor binding of that type not seen yet, just remember it
                seenBindings.put(name, binding.valuesWithDefaults(index));
            }
        }
    }

    private static void checkInterceptorFieldsAndMethods(ClassInfo interceptorClass, BeanDeployment beanDeployment) {
        ClassInfo aClass = interceptorClass;
        while (aClass != null) {
            for (MethodInfo method : aClass.methods()) {
                if (beanDeployment.hasAnnotation(method, DotNames.PRODUCES)) {
                    throw new DefinitionException("Interceptor declares a producer method: " + interceptorClass);
                }
                // the following 3 checks rely on the annotation store returning parameter annotations for methods

View on GitHub (pinned to e1c734241f)

Solutions

  1. Make the member values identical in all declarations of the binding annotation on the class hierarchy, or remove the duplicate declaration.
  2. Mark the differing member with @NonBinding if its value should not participate in binding equality.
  3. Restructure so the binding appears only once (e.g. only on the leaf class), and consider @Repeatable bindings if multiple distinct bindings are actually wanted.

Example fix

// before
@Logged(level = "INFO") class Base { }
@Logged(level = "DEBUG") class Child extends Base { }
// after
@Logged(level = "DEBUG") class Child extends Base { } // remove annotation from Base, or make values equal
Defensive patterns

Strategy: validation

Validate before calling

// Check the class hierarchy for repeated non-repeatable bindings with conflicting members
java.util.Map<Class<?>, java.lang.annotation.Annotation> seen = new java.util.HashMap<>();
for (Class<?> c = Child.class; c != null && c != Object.class; c = c.getSuperclass()) {
    for (java.lang.annotation.Annotation a : c.getAnnotations()) {
        if (a.annotationType().isAnnotationPresent(jakarta.interceptor.InterceptorBinding.class)
            && !a.annotationType().isAnnotationPresent(java.lang.annotation.Repeatable.class)) {
            java.lang.annotation.Annotation prev = seen.putIfAbsent(a.annotationType(), a);
            if (prev != null && !prev.equals(a)) {
                throw new IllegalStateException("Conflicting binding values: " + prev + " vs " + a);
            }
        }
    }
}

Prevention

When it happens

Trigger: The same binding annotation appears multiple times on a class hierarchy (e.g. on both the interceptor class and its superclass, or repeated via inheritance) with non-binding member values that differ after applying annotation-member defaults. Non-binding and @NonBinding members are excluded from the comparison.

Common situations: Subclassing an interceptor or bean that already carries a binding annotation and re-annotating the subclass with different member values (e.g. @Logged(level="INFO") on the parent, @Logged(level="DEBUG") on the child); changing member defaults between versions.

Related errors


AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05). Data as JSON: /api/errors/8fec8ebcae352032. Report an issue: GitHub.