quarkusio/quarkus · error · RuntimeException

io.quarkus.oidc.client.OidcClientFilter annotation can be ap

Error message

io.quarkus.oidc.client.OidcClientFilter annotation can be applied either on class %s or its methods

What it means

During OIDC client filter build-step processing, a REST client interface was found where the @OidcClientFilter annotation appears both on the class and on at least one of its methods. Quarkus cannot decide unambiguously whether to register the filter per class or per method, so the build fails with this RuntimeException.

Source

Thrown at extensions/oidc-client/deployment/src/main/java/io/quarkus/oidc/client/deployment/OidcClientFilterDeploymentHelper.java:251

                                .filter(ai -> ai.target().kind() == AnnotationTarget.Kind.CLASS)
                                .map(ai -> ai.target().asClass())
                                .toList());
                        result.addAll(index
                                .getAnnotations(registerProvidersClass).stream()
                                .filter(ai -> ai.value() != null)
                                .filter(ai -> registersDeclaringClass(ai, declaringClassName))
                                .filter(ai -> ai.target().kind() == AnnotationTarget.Kind.CLASS)
                                .map(ai -> ai.target().asClass())
                                .toList());
                    }
                }
            }
        }
        return result;
    }

    private static void throwBothMethodAndClassAnnotated(AnnotationInstance instance) {
        throw new RuntimeException(OidcClientFilter.class.getName() + " annotation can be applied either on class "
                + getTargetRestClient(instance) + " or its methods");
    }

    private static boolean registersDeclaringClass(AnnotationInstance ai, DotName declaringClassName) {
        return Arrays.stream(ai.value().asNestedArray())
                .anyMatch(nestedAi -> nestedAi.value().asClass().name().equals(declaringClassName));
    }
}

View on GitHub (pinned to e1c734241f)

Solutions

  1. Remove @OidcClientFilter from the method(s) and keep it only on the class to apply to all methods.
  2. Remove @OidcClientFilter from the class and keep it only on the specific method(s) that need it.
  3. Split the REST client into two interfaces: one class-annotated and one method-annotated, if both behaviors are genuinely needed.

Example fix

// before
@OidcClientFilter
public interface MyClient {
    @OidcClientFilter
    @GET String get();
}

// after
@OidcClientFilter
public interface MyClient {
    @GET String get();
}
Defensive patterns

Strategy: validation

Validate before calling

boolean classAnnotated = Arrays.stream(MyClient.class.getAnnotations()).anyMatch(a -> a instanceof OidcClientFilter);
boolean methodAnnotated = Arrays.stream(MyClient.class.getMethods()).anyMatch(m -> m.isAnnotationPresent(OidcClientFilter.class));
if (classAnnotated && methodAnnotated) throw new IllegalStateException("@OidcClientFilter on both class and method");

Prevention

When it happens

Trigger: A REST client interface annotated with @OidcClientFilter at class level AND with @OidcClientFilter on one of its methods, encountered while getOrCreateNamedTokensProducerFor processes annotation instances.

Common situations: Copy-paste adding the annotation to both the interface and an individual method; combining a global class-level filter with an attempt to customize a single method.

Related errors


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