quarkusio/quarkus · error · DeploymentException

Cannot have more than one of @PathParam, @QueryParam, @Heade

Error message

Cannot have more than one of @PathParam, @QueryParam, @HeaderParam, @FormParam, @CookieParam, @BeanParam, @Context on 

What it means

A single parameter (or field of a bean) may carry at most one request-data annotation among @PathParam, @QueryParam, @HeaderParam, @FormParam, @CookieParam, @BeanParam, @Context (and their @Rest* counterparts). Binding one parameter to two sources is ambiguous, so EndpointIndexer fails deployment via the moreThanOne(...) check.

Source

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

        AnnotationInstance cookieParam = anns.get(COOKIE_PARAM);
        AnnotationInstance restPathParam = anns.get(REST_PATH_PARAM);
        AnnotationInstance restQueryParam = anns.get(REST_QUERY_PARAM);
        AnnotationInstance restHeaderParam = anns.get(REST_HEADER_PARAM);
        AnnotationInstance restFormParam = anns.get(REST_FORM_PARAM);
        AnnotationInstance restMatrixParam = anns.get(REST_MATRIX_PARAM);
        AnnotationInstance restCookieParam = anns.get(REST_COOKIE_PARAM);
        AnnotationInstance contextParam = anns.get(CONTEXT);
        AnnotationInstance defaultValueAnnotation = anns.get(DEFAULT_VALUE);
        AnnotationInstance suspendedAnnotation = anns.get(SUSPENDED);
        boolean convertible = false;
        if (defaultValueAnnotation != null) {
            builder.setDefaultValue(defaultValueAnnotation.value().asString());
        }
        if (handleCustomParameter(anns, builder, paramType, field, methodContext)) {
            return builder;
        } else if (moreThanOne(pathParam, queryParam, headerParam, formParam, cookieParam, contextParam, beanParam,
                restPathParam, restQueryParam, restHeaderParam, restFormParam, restCookieParam)) {
            throw new DeploymentException(
                    "Cannot have more than one of @PathParam, @QueryParam, @HeaderParam, @FormParam, @CookieParam, @BeanParam, @Context on "
                            + builder.getErrorLocation());
        } else if (pathParam != null) {
            builder.setName(pathParam.value().asString());
            builder.setType(ParameterType.PATH);
            convertible = true;
        } else if (restPathParam != null) {
            builder.setName(parameterNameOrFail(restPathParam.value(), sourceName, "RestPath", builder.getErrorLocation()));
            builder.setType(ParameterType.PATH);
            convertible = true;
        } else if (queryParam != null) {
            builder.setName(queryParam.value().asString());
            builder.setType(ParameterType.QUERY);
            builder.setSeparator(getSeparator(anns));
            convertible = true;
        } else if (restQueryParam != null) {
            builder.setName(parameterNameOrFail(restQueryParam.value(), sourceName, "RestQuery", builder.getErrorLocation()));
            builder.setType(ParameterType.QUERY);

View on GitHub (pinned to e1c734241f)

Solutions

  1. Keep exactly one binding annotation per parameter, matching the intended source (path, query, header, form, cookie, or context)
  2. If the value must come from multiple sources, declare separate parameters and combine them in the method body
  3. Check custom handleCustomParameter logic/annotations if the parameter intentionally maps via a framework extension

Example fix

// before
public String get(@QueryParam("q") @HeaderParam("x-q") String value) { ... }
// after
public String get(@QueryParam("q") String query, @HeaderParam("x-q") String header) { ... }
Defensive patterns

Strategy: validation

Validate before calling

static final Set<Class<?>> BINDING = Set.of(
    PathParam.class, QueryParam.class, HeaderParam.class,
    FormParam.class, CookieParam.class, BeanParam.class, Context.class);
static void validateOneBindingPerParam(Method m) {
    for (java.lang.reflect.Parameter p : m.getParameters()) {
        long n = java.util.Arrays.stream(p.getAnnotations())
            .filter(a -> BINDING.contains(a.annotationType())
                || a.annotationType().getName().startsWith("io.quarkus.resteasy.reactive"))
            .map(a -> a.annotationType().getSimpleName().replace("Rest", ""))
            .distinct().count();
        if (n > 1) throw new IllegalStateException("Multiple binding annotations on " + p);
    }
}

Prevention

When it happens

Trigger: A resource method parameter is annotated with two or more of the binding annotations, e.g. @QueryParam("q") @HeaderParam("x") String v, or a @RestForm that also has @PathParam; also triggered inside @BeanParam field processing.

Common situations: Copy-paste adding a second annotation to switch binding source without removing the first; accidental multi-annotation after merge conflicts; annotation-adding IDE quick-fixes stacking binding annotations.

Related errors


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