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
- Remove the separate body parameter and model its data as @RestForm fields instead
- Keep the body parameter and remove all @FormParam/@RestForm annotations, passing form values through the body object (e.g. a MultiValuedMap or @MultipartForm bean)
- 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
- Keep one data-source strategy per endpoint: either form fields or a body object, never both
- Use @MultipartForm beans or MultiValuedMap to bundle form data instead of mixing parameters
- Add an ArchUnit or annotation-processing check in CI to catch mixed form/body signatures
- Review resource method signatures when migrating between form and JSON payloads
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
- '@FormParam' and '@RestForm' can only be used on methods ann
- Parameter: ${i} of the constructor of class '${resourceDotNa
- Unable to load handled exception type ${i.getProvidedType()}
- Endpoints that produce a Multipart result can only be used o
- Failed to process method '%s#%s'. Reason: %s
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/bea3eed88dff9cd3.
Report an issue: GitHub.