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' as its return type.

What it means

generateResponseFilterForSuspendedMethod handles Kotlin suspend @ServerResponseFilter methods. It unwraps the Continuation return type and requires it to be void (Unit in Kotlin); response filters must not return a value — they mutate the ContainerResponseContext or return via the context. A non-void result throws this RuntimeException (note the message says 'request filter' due to a copied message string, but it is raised for response filters).

Source

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

                        + "' is not allowed");
            }
        }
        return targetMethodParamHandles;
    }

    String generateContainerResponseFilter(MethodInfo targetMethod, ClassOutput classOutput) {
        checkModifiers(targetMethod, ResteasyReactiveServerDotNames.SERVER_RESPONSE_FILTER);
        if (KotlinUtils.isSuspendMethod(targetMethod)) {
            return generateResponseFilterForSuspendedMethod(targetMethod, classOutput, isOptionalFilter.test(targetMethod));
        }
        return generateStandardContainerResponseFilter(targetMethod, classOutput, isOptionalFilter.test(targetMethod));
    }

    private String generateResponseFilterForSuspendedMethod(MethodInfo targetMethod, ClassOutput classOutput,
            boolean checkForOptionalBean) {
        DotName returnDotName = determineReturnDotNameOfSuspendMethod(targetMethod);
        if (!returnDotName.equals(VOID)) {
            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' as its return type.");
        }
        String generatedClassName = getGeneratedClassName(targetMethod, ResteasyReactiveServerDotNames.SERVER_RESPONSE_FILTER);
        ClassInfo declaringClass = targetMethod.declaringClass();
        DotName declaringClassName = declaringClass.name();
        try (ClassCreator cc = ClassCreator.builder().classOutput(classOutput)
                .className(generatedClassName)
                .superClass(ABSTRACT_SUSPENDED_RES_FILTER)
                .build()) {
            FieldDescriptor delegateField = generateConstructorAndDelegateField(cc, declaringClass,
                    ABSTRACT_SUSPENDED_RES_FILTER, additionalBeanAnnotations, checkForOptionalBean);

            // generate the implementation of the filter method
            MethodCreator doFilterMethod = cc.getMethodCreator("doFilter", Object.class.getName(),
                    ResteasyReactiveContainerRequestContext.class.getName(), ContainerResponseContext.class.getName(),
                    ResteasyReactiveDotNames.CONTINUATION.toString());

View on GitHub (pinned to e1c734241f)

Solutions

  1. Change the suspend response filter to return Unit and mutate ContainerResponseContext (e.g. setEntity, setStatus) instead of returning a value
  2. Use a non-suspend response filter returning void if no suspension is needed
  3. Remove @ServerResponseFilter if the method is not a response filter

Example fix

// before (Kotlin)
@ServerResponseFilter
suspend fun filter(response: ContainerResponseContext): Response { ... }

// after
@ServerResponseFilter
suspend fun filter(response: ContainerResponseContext) {
    response.statusInfo = Response.Status.OK
}
Defensive patterns

Strategy: validation

Validate before calling

// Kotlin: suspend response filter must return Unit
val m = clazz.declaredMethods.first { it.isAnnotationPresent(ServerResponseFilter::class.java) }
if (m.kotlinFunction?.isSuspend == true && m.returnType != Void.TYPE) {
    throw new IllegalStateException("Suspend response filter must return Unit: $m")
}

Type guard

fun isValidSuspendResponseFilter(m: Method): Boolean = m.returnType == Void.TYPE

Prevention

When it happens

Trigger: A Kotlin suspend fun annotated with @ServerResponseFilter whose return type after Continuation unwrapping is not void/Unit — e.g. suspend fun filter(ctx: ContainerResponseContext): Response.

Common situations: Kotlin developers returning the (possibly modified) response from suspend response filters, assuming return-based mutation like request filters allow with Response.

Related errors


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