quarkusio/quarkus · error · RestClientDefinitionException

Classes used in @SseEventFilter must have a no-args construc

Error message

Classes used in @SseEventFilter must have a no-args constructor. Offending class is '${class}'

What it means

Classes referenced by the `@SseEventFilter` annotation on a REST Client interface are instantiated reflectively by the runtime and therefore must have a public no-args constructor. During build, Quarkus indexes the filter class and throws RestClientDefinitionException if it lacks one.

Source

Thrown at extensions/resteasy-reactive/rest-client/deployment/src/main/java/io/quarkus/rest/client/reactive/deployment/RestClientReactiveProcessor.java:536

        if (instances.isEmpty()) {
            return;
        }

        List<String> filterClassNames = new ArrayList<>(instances.size());
        for (AnnotationInstance instance : instances) {
            if (instance.target().kind() != AnnotationTarget.Kind.METHOD) {
                continue;
            }
            if (instance.value() == null) {
                continue; // can't happen
            }
            Type filterType = instance.value().asClass();
            DotName filterClassName = filterType.name();
            ClassInfo filterClassInfo = index.getClassByName(filterClassName.toString());
            if (filterClassInfo == null) {
                log.warn("Unable to find class '" + filterType.name() + "' in index");
            } else if (!filterClassInfo.hasNoArgsConstructor()) {
                throw new RestClientDefinitionException(
                        "Classes used in @SseEventFilter must have a no-args constructor. Offending class is '"
                                + filterClassName + "'");
            } else {
                filterClassNames.add(filterClassName.toString());
            }
        }
        reflectiveClasses.produce(ReflectiveClassBuildItem.builder(filterClassNames.toArray(new String[0]))
                .reason(getClass().getName())
                .build());
    }

    @BuildStep
    void determineRegisteredRestClients(
            CombinedIndexBuildItem combinedIndexBuildItem,
            RestClientsBuildTimeConfig clientsConfig,
            BuildProducer<RegisteredRestClientBuildItem> producer) {
        IndexView index = combinedIndexBuildItem.getIndex();
        Set<DotName> seen = new HashSet<>();

View on GitHub (pinned to e1c734241f)

Solutions

  1. Add a public no-args constructor to the filter class
  2. Move required state into instance fields set from the constructor-less init or static config
  3. Replace the filter with a new class dedicated to client-side SSE filtering

Example fix

// before
class MyFilter {
    MyFilter(Logger log) { ... }
}
// after
class MyFilter {
    public MyFilter() {}
    MyFilter(Logger log) { ... }
}
Defensive patterns

Strategy: validation

Validate before calling

// Fail fast in a unit test before building the app
SseEventFilter f = MyClient.class.getMethod("stream").getAnnotation(SseEventFilter.class);
assertThrows(null, () -> {
  var ctor = f.value().getDeclaredConstructor();
});
// or at runtime:
boolean ok = java.lang.reflect.Modifier.isPublic(MyFilter.class.getDeclaredConstructor().getModifiers());

Prevention

When it happens

Trigger: Annotating a client interface method with `@SseEventFilter(MyFilter.class)` where MyFilter only has parameterized constructors (e.g. a constructor taking a Logger or config).

Common situations: Reusing a JAX-RS server-side filter class that has dependency-injected constructors; refactoring a filter to add constructor parameters.

Related errors


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