quarkusio/quarkus · error · DeploymentException
${exposedEndpoint} is declared by :${endpointConfigs}
Error message
${exposedEndpoint} is declared by :${endpointConfigs} What it means
Quarkus RESTEasy Reactive detects when the same HTTP method + path combination is declared by more than one resource method (including via class-level @Path prefixes). The aggregated message names the duplicated endpoint and the competing resource/method configurations. If the REST config failOnDuplicate is enabled, deployment throws this DeploymentException; otherwise it only logs a warning.
Source
Thrown at extensions/resteasy-reactive/rest/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ResteasyReactiveProcessor.java:1686
@BuildStep
@Record(value = ExecutionTime.STATIC_INIT)
public FilterBuildItem addDefaultAuthFailureHandler(ResteasyReactiveRecorder recorder,
ResteasyReactiveDeploymentBuildItem deployment,
Optional<ObservabilityIntegrationBuildItem> observabilityIntegrationBuildItem) {
// replace default auth failure handler added by vertx-http so that our exception mappers can customize response
return new FilterBuildItem(
recorder.defaultAuthFailureHandler(deployment.getDeployment(), observabilityIntegrationBuildItem.isPresent()),
SecurityHandlerPriorities.AUTHENTICATION - 1);
}
private void checkForDuplicateEndpoint(ResteasyReactiveConfig config, Map<String, List<EndpointConfig>> allMethods) {
String message = allMethods.values().stream()
.map(this::getDuplicateEndpointMessage)
.filter(Objects::nonNull)
.collect(Collectors.joining());
if (!message.isEmpty()) {
if (config.failOnDuplicate()) {
throw new DeploymentException(message);
}
log.warn(message);
}
}
private void addResourceMethodByPath(Map<String, List<EndpointConfig>> allMethods, String path, ClassInfo info,
ResourceMethod rm) {
allMethods.computeIfAbsent(getEndpointClassifier(rm, path), key -> new ArrayList<>())
.addAll(getEndpointConfigs(path, info, rm));
}
private String getEndpointClassifier(ResourceMethod resourceMethod, String path) {
String fullPath = (path.equals("/") ? "" : path) + resourceMethod.getPath();
return resourceMethod.getHttpMethod() + " " + normalizePathParamNames(fullPath);
}
/**
* Replaces path parameter names with a fixed placeholder so that structurally equivalentView on GitHub (pinned to e1c734241f)
Solutions
- Change the @Path of one of the conflicting methods so each endpoint is unique
- Change the HTTP verb of one method if the duplication was unintended
- Remove the dead/duplicate method if both implementations are identical
- If duplicates are acceptable, disable the strict behavior via the RESTEasy Reactive failOnDuplicate config option (deployment then only warns)
- Identify the offending classes from the endpointConfigs listed in the message and refactor path structure
Example fix
// before
@Path("/pets")
class A { @GET @Path("{id}") Pet get(long id) {...} }
class B { @GET @Path("/pets/{id}") Pet find(long id) {...} }
// after
class B { @GET @Path("/pets/details/{id}") Pet find(long id) {...} } Defensive patterns
Strategy: validation
Validate before calling
// pre-build scan for duplicate method+path declarations
Map<String, List<String>> seen = new HashMap<>();
for (Class<?> resource : resourceClasses) {
String basePath = resource.getAnnotation(jakarta.ws.rs.Path.class).value();
for (Method m : resource.getDeclaredMethods()) {
jakarta.ws.rs.Path p = m.getAnnotation(jakarta.ws.rs.Path.class);
for (String verb : List.of("GET","POST","PUT","DELETE")) {
try {
if (m.isAnnotationPresent((Class) Class.forName("jakarta.ws.rs." + verb))) {
String key = verb + " " + basePath + (p == null ? "" : p.value());
seen.computeIfAbsent(key, k -> new ArrayList<>()).add(resource.getName() + "#" + m.getName());
}
} catch (ClassNotFoundException ignored) {}
}
}
}
seen.entrySet().stream().filter(e -> e.getValue().size() > 1)
.forEach(e -> System.err.println("Duplicate endpoint: " + e.getKey() + " -> " + e.getValue())); Prevention
- Keep endpoint paths unique across all resource classes; centralize shared path prefixes
- Run an integration test that boots the app with failOnDuplicate enabled in CI
- After merging modules, review @Path values for collisions
- Generate an OpenAPI document at build time to surface duplicate operations early
When it happens
Trigger: Two resource methods (possibly in different classes) resolve to the same HTTP verb and request path, and the duplicate-endpoint failure config option (failOnDuplicate in the RESTEasy Reactive server config) is true. The check runs on the fully aggregated endpoint map during the build.
Common situations: Copy-pasting a resource method without changing the @Path value; merging modules where two services declare the same route; Locator/Path-interface combinations that accidentally collide; enabling the fail-on-duplicate flag for the first time and surfacing pre-existing collisions.
Related errors
- Parameter: ${i} of the constructor of class '${resourceDotNa
- Parameter: ${i} of the constructor of class '${resourceDotNa
- Unsupported type '${jaxRSAnnotationOfParam.name()}' used as
- Resource classes that use field injection for REST parameter
- Resource classes that use field injection for REST parameter
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/b1c848aae37ba714.
Report an issue: GitHub.