quarkusio/quarkus · error · RuntimeException

Suspend method '${targetMethod.name()} of class '${targetMet

Error message

Suspend method '${targetMethod.name()} of class '${targetMethod.declaringClass().name()}' cannot be used as a request filter as it does not declare 'void', 'Response', 'RestResponse' as its return type.

What it means

CustomFilterGenerator's generateRequestFilterForSuspendedMethod handles Kotlin suspend request filter methods by unwrapping their Continuation return type. After unwrapping, the underlying return type must be void, Response, or RestResponse; anything else throws this RuntimeException and fails the Quarkus build.

Source

Thrown at independent-projects/resteasy-reactive/server/processor/src/main/java/org/jboss/resteasy/reactive/server/processor/generation/filters/CustomFilterGenerator.java:110

        checkModifiers(targetMethod, ResteasyReactiveServerDotNames.SERVER_REQUEST_FILTER);
        if (KotlinUtils.isSuspendMethod(targetMethod)) {
            return generateRequestFilterForSuspendedMethod(targetMethod, classOutput, isOptionalFilter.test(targetMethod));
        }
        return generateStandardRequestFilter(targetMethod, classOutput, isOptionalFilter.test(targetMethod));
    }

    private String generateRequestFilterForSuspendedMethod(MethodInfo targetMethod, ClassOutput classOutput,
            boolean checkForOptionalBean) {
        DotName returnDotName = determineReturnDotNameOfSuspendMethod(targetMethod);
        ReturnType returnType;
        if (returnDotName.equals(VOID)) {
            returnType = ReturnType.VOID;
        } else if (returnDotName.equals(RESPONSE)) {
            returnType = ReturnType.RESPONSE;
        } else if (returnDotName.equals(REST_RESPONSE)) {
            returnType = ReturnType.REST_RESPONSE;
        } else {
            throw new RuntimeException("Suspend method '" + targetMethod.name() + " of class '"
                    + targetMethod.declaringClass().name()
                    + "' cannot be used as a request filter as it does not declare 'void', 'Response', 'RestResponse' as its return type.");
        }

        String generatedClassName = getGeneratedClassName(targetMethod, ResteasyReactiveServerDotNames.SERVER_REQUEST_FILTER);
        ClassInfo declaringClass = targetMethod.declaringClass();
        DotName declaringClassName = declaringClass.name();
        try (ClassCreator cc = ClassCreator.builder().classOutput(classOutput)
                .className(generatedClassName)
                .superClass(ABSTRACT_SUSPENDED_REQ_FILTER)
                .build()) {
            FieldDescriptor delegateField = generateConstructorAndDelegateField(cc, declaringClass,
                    ABSTRACT_SUSPENDED_REQ_FILTER, additionalBeanAnnotations, checkForOptionalBean);

            // generate the implementation of the 'doFilter' method
            MethodCreator doFilterMethod = cc.getMethodCreator("doFilter", Object.class.getName(),
                    ResteasyReactiveContainerRequestContext.class.getName(), ResteasyReactiveDotNames.CONTINUATION.toString());
            ResultHandle delegate = doFilterMethod.readInstanceField(delegateField, doFilterMethod.getThis());

View on GitHub (pinned to e1c734241f)

Solutions

  1. Change the suspend filter to return Unit (void) and mutate the ContainerRequestContext or call context.abortWith(...) instead
  2. Return Response or RestResponse from the suspend filter for early termination
  3. If you want to accept/abort without a response, use the non-suspend filter returning void or the documented Boolean-based pattern
  4. Remove @ServerRequestFilter if the method is not a filter

Example fix

// before (Kotlin)
@ServerRequestFilter
suspend fun filter(ctx: ContainerRequestContext): String { ... }

// after
@ServerRequestFilter
suspend fun filter(ctx: ContainerRequestContext) {
    ctx.abortWith(Response.status(401).build())
}
Defensive patterns

Strategy: validation

Validate before calling

// Kotlin: check suspend request filter return before build
val m = clazz.declaredMethods.first { it.isAnnotationPresent(ServerRequestFilter::class.java) }
val allowed = setOf(Void.TYPE, Response::class.java, RestResponse::class.java)
require(m.returnType in allowed) { "Suspend request filter must return Unit, Response, or RestResponse: $m" }

Type guard

fun isValidSuspendRequestFilter(m: Method): Boolean =
    m.returnType == Void.TYPE || m.returnType == Response::class.java || m.returnType == RestResponse::class.java

Prevention

When it happens

Trigger: A Kotlin suspend fun used as a @ServerRequestFilter whose Continuation-wrapped return type is something other than void/Unit, Response, or RestResponse — e.g. returning String, Int, or a custom type.

Common situations: Kotlin developers writing suspend filters that return a value instead of mutating the request context, or returning Boolean (the dedicated @ServerRequestFilter filterOnly variant should be used instead).

Related errors


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