quarkusio/quarkus · error · IllegalArgumentException
Interceptor class ${interceptorClass} is not a CDI bean. Onl
Error message
Interceptor class ${interceptorClass} is not a CDI bean. Only CDI beans can be used as gRPC server/client interceptors. Add one of the scope-defining annotations (@Singleton, @ApplicationScoped, @RequestScoped) on the interceptor class. What it means
Quarkus gRPC resolves per-service server/client interceptor classes from the CDI container at runtime. This error is thrown when Arc.container().instance(interceptorClass, Any.Literal.INSTANCE) returns an instance that is null, meaning the class is not a managed CDI bean. Only beans with a scope-defining annotation can be instantiated and injected by CDI, so plain classes registered as interceptors are rejected with this IllegalArgumentException.
Source
Thrown at extensions/grpc/runtime/src/main/java/io/quarkus/grpc/runtime/Interceptors.java:35
public static final int DUPLICATE_CONTEXT = Integer.MAX_VALUE - 5;
public static final int REQUEST_CONTEXT = Integer.MAX_VALUE - 50;
public static final int ROUTING_CONTEXT = Integer.MAX_VALUE - 55;
public static final int BLOCKING_HANDLER = Integer.MAX_VALUE - 60;
public static final int EXCEPTION_HANDLER = Integer.MAX_VALUE - 70;
static <T> List<T> getSortedPerServiceInterceptors(String name, Set<Class<?>> interceptorClasses) {
if (interceptorClasses.isEmpty()) {
return Collections.emptyList();
}
List<T> interceptors = new ArrayList<>();
for (Class<?> interceptorClass : interceptorClasses) {
// Note that additional qualifiers are ignored - @Any is required
InstanceHandle<?> interceptorInstance = Arc.container().instance(interceptorClass, Any.Literal.INSTANCE);
@SuppressWarnings("unchecked")
T interceptor = (T) interceptorInstance.get();
if (interceptor == null) {
throw new IllegalArgumentException("Interceptor class " + interceptorClass + " is not a CDI bean. " +
"Only CDI beans can be used as gRPC server/client interceptors. Add one of the scope-defining annotations"
+
" (@Singleton, @ApplicationScoped, @RequestScoped) on the interceptor class.");
}
interceptors.add(interceptor);
}
interceptors.sort(Interceptors.INTERCEPTOR_COMPARATOR);
return interceptors;
}
static <T> List<T> getSortedPerServiceInterceptors(Set<Class<?>> interceptorClasses) {
if (interceptorClasses.isEmpty()) {
return Collections.emptyList();
}
List<T> interceptors = new ArrayList<>();
for (Class<?> interceptorClass : interceptorClasses) {
// Note that additional qualifiers are ignored - @Any is requiredView on GitHub (pinned to e1c734241f)
Solutions
- Annotate the interceptor class with a scope-defining annotation such as @Singleton (preferred for stateless interceptors).
- If the interceptor needs dependencies, use @ApplicationScoped and @Inject fields.
- Verify the class is part of the Quarkus application archive (in src/main/java of the app or an indexed library JAR with a Jandex index).
- Confirm the fully-qualified class name in the configuration property matches the annotated class exactly (no typos).
Example fix
// before
public class TracingInterceptor implements ServerInterceptor { ... }
// after
import jakarta.inject.Singleton;
@Singleton
public class TracingInterceptor implements ServerInterceptor { ... } Defensive patterns
Strategy: validation
Validate before calling
// before configuring the interceptor
Class<?> clazz = TracingInterceptor.class;
if (!clazz.isAnnotationPresent(jakarta.inject.Singleton.class)
&& !clazz.isAnnotationPresent(jakarta.enterprise.context.ApplicationScoped.class)
&& !clazz.isAnnotationPresent(jakarta.enterprise.context.RequestScoped.class)) {
throw new IllegalStateException(clazz + " must be a scope-annotated CDI bean");
} Try / catch
try {
ManagedChannel c = grpcClient.getChannel();
} catch (IllegalArgumentException e) {
if (e.getMessage() != null && e.getMessage().contains("not a CDI bean")) {
log.error("Add @Singleton/@ApplicationScoped to the interceptor class: " + e.getMessage());
} else { throw e; }
} Prevention
- Always annotate gRPC interceptors with @Singleton by default.
- Keep interceptors in the application archive so ArC indexes them.
- Add an integration test that starts the app and resolves every configured interceptor at boot.
When it happens
Trigger: Registering a class in quarkus.grpc.clients.<name>.interceptor or quarkus.grpc.server.interceptors config (or via @GrpcService/Interceptors registration) where the class has no CDI scope annotation, so the Arc container lookup by the interceptor class returns null in getSortedPerServiceInterceptors.
Common situations: Developer writes a plain `implements ServerInterceptor` class without @Singleton/@ApplicationScoped, copies a gRPC interceptor from a non-Quarkus project, or the class is a CDI bean but in a package excluded from bean discovery (no beans.xml needed nowadays but must be in an indexed archive), or the class has a non-default-constructor pattern Arc cannot resolve.
Related errors
- Unable to find the GrpcClientConfigProvider
- Unable to retrieve the gRPC Channel ${name}
- Cannot find a @Transactional annotation
- Multiple @AroundInvoke interceptor methods declared on class
- Multiple @AroundConstruct interceptor methods declared on cl
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/fe6d65a06f56be62.
Report an issue: GitHub.