quarkusio/quarkus · error · DeploymentException

'@FormParam' and '@RestForm' cannot be used in a resource me

Error message

'@FormParam' and '@RestForm' cannot be used in a resource method that contains a body parameter. Offending method is '%s#%s'

What it means

RESTEasy Reactive rejects resource methods that combine @FormParam/@RestForm parameters with a body parameter (unless the body is MultiValuedMap or String). Form data itself is the request body, so a second body parameter makes the request entity ambiguous and cannot be mapped. The deployment-time check in EndpointIndexer.createResourceMethod fails fast with a DeploymentException naming the offending method.

Source

Thrown at independent-projects/resteasy-reactive/common/processor/src/main/java/org/jboss/resteasy/reactive/common/processor/EndpointIndexer.java:668

                methodParameters[i] = createMethodParameter(currentClassInfo, actualEndpointInfo, encoded, paramType,
                        parameterResult, name, defaultValue, type, elementType, single,
                        AsmUtil.getSignature(paramType, typeArgMapper), fileFormNames);

                if (type == ParameterType.BEAN
                        || type == ParameterType.MULTI_PART_FORM) {
                    // transform the bean param
                    formParamRequired |= handleBeanParam(actualEndpointInfo, paramType, methodParameters, i, fileFormNames);
                } else if (type == ParameterType.FORM || type == ParameterType.MULTI_PART_DATA_INPUT
                        || type == ParameterType.ENTITY_PART_LIST) {
                    formParamRequired = true;
                }
            }

            if (formParamRequired) {
                if (bodyParamType != null
                        && !bodyParamType.name().equals(ResteasyReactiveDotNames.MULTI_VALUED_MAP)
                        && !bodyParamType.name().equals(ResteasyReactiveDotNames.STRING)) {
                    throw new DeploymentException(String.format(
                            "'@FormParam' and '@RestForm' cannot be used in a resource method that contains a body parameter. Offending method is "
                                    + "'%s#%s'",
                            currentMethodInfo.declaringClass().name(), currentMethodInfo));
                }
                boolean validConsumes = false;
                if (consumes != null && consumes.length > 0) {
                    for (String c : consumes) {
                        if (c.startsWith(MediaType.MULTIPART_FORM_DATA)
                                || c.startsWith(MediaType.APPLICATION_FORM_URLENCODED)) {
                            validConsumes = true;
                            break;
                        }
                    }
                    // TODO: does it make sense to default to MediaType.MULTIPART_FORM_DATA when no consumes is set?
                    if (!validConsumes) {
                        throw new DeploymentException(String.format(
                                "'@FormParam' and '@RestForm' can only be used on methods annotated with '@Consumes(MediaType.MULTIPART_FORM_DATA)' '@Consumes(MediaType.APPLICATION_FORM_URLENCODED)'. Offending method is "
                                        + "'%s#%s'",

View on GitHub (pinned to e1c734241f)

Solutions

  1. Remove the separate body parameter and model its data as @RestForm fields instead
  2. Keep the body parameter and remove all @FormParam/@RestForm annotations, passing form values through the body object (e.g. a MultiValuedMap or @MultipartForm bean)
  3. If using MultiValuedMap, change the body parameter type to MultiValuedMap<String,String>, which is explicitly allowed alongside form params

Example fix

// before
@POST
@Consumes(MediaType.MULTIPART_FORM_DATA)
public String upload(@RestForm String name, MyDto dto) { ... }
// after
@POST
@Consumes(MediaType.MULTIPART_FORM_DATA)
public String upload(@RestForm String name, @RestForm String other) { ... }
Defensive patterns

Strategy: validation

Validate before calling

// Deployment-time check: scan resource methods before build
Method[] methods = MyResource.class.getDeclaredMethods();
for (Method m : methods) {
    boolean hasForm = java.util.Arrays.stream(m.getParameterAnnotations())
        .flatMap(java.util.Arrays::stream)
        .anyMatch(a -> a.annotationType().getSimpleName().matches("(FormParam|RestForm)"));
    long bodyParams = java.util.Arrays.stream(m.getParameters())
        .filter(p -> java.util.Arrays.stream(p.getAnnotations())
            .noneMatch(a -> a.annotationType().getSimpleName().matches("(PathParam|QueryParam|HeaderParam|FormParam|CookieParam|BeanParam|Context|Rest.*|MultipartForm)")))
        .count();
    if (hasForm && bodyParams > 0) throw new IllegalStateException("Form params + body param in " + m);
}

Prevention

When it happens

Trigger: A JAX-RS/REST endpoint method declares both form parameter annotations (e.g. @RestForm String name) and another non-form body parameter (e.g. an unannotated POJO, InputStream, byte[], or @MultipartForm type other than MultiValuedMap/String), then the Quarkus application is built/deployed.

Common situations: Mixing old servlet-style form parsing with a JSON/POJO body; migrating a method to @RestForm while leaving an existing body parameter in place; accidentally adding @RestForm fields to a method that already accepts a @MultipartForm or entity object.

Related errors


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