{"record":{"id":"b1c848aae37ba714","repo":"quarkusio/quarkus","slug":"exposedendpoint-is-declared-by-endpointconfi","errorCode":null,"errorMessage":"${exposedEndpoint} is declared by :${endpointConfigs}","messagePattern":"(.+?) is declared by :(.+?)","errorType":"exception","errorClass":"DeploymentException","httpStatus":null,"severity":"error","filePath":"extensions/resteasy-reactive/rest/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ResteasyReactiveProcessor.java","lineNumber":1686,"sourceCode":"    @BuildStep\n    @Record(value = ExecutionTime.STATIC_INIT)\n    public FilterBuildItem addDefaultAuthFailureHandler(ResteasyReactiveRecorder recorder,\n            ResteasyReactiveDeploymentBuildItem deployment,\n            Optional<ObservabilityIntegrationBuildItem> observabilityIntegrationBuildItem) {\n        // replace default auth failure handler added by vertx-http so that our exception mappers can customize response\n        return new FilterBuildItem(\n                recorder.defaultAuthFailureHandler(deployment.getDeployment(), observabilityIntegrationBuildItem.isPresent()),\n                SecurityHandlerPriorities.AUTHENTICATION - 1);\n    }\n\n    private void checkForDuplicateEndpoint(ResteasyReactiveConfig config, Map<String, List<EndpointConfig>> allMethods) {\n        String message = allMethods.values().stream()\n                .map(this::getDuplicateEndpointMessage)\n                .filter(Objects::nonNull)\n                .collect(Collectors.joining());\n        if (!message.isEmpty()) {\n            if (config.failOnDuplicate()) {\n                throw new DeploymentException(message);\n            }\n            log.warn(message);\n        }\n    }\n\n    private void addResourceMethodByPath(Map<String, List<EndpointConfig>> allMethods, String path, ClassInfo info,\n            ResourceMethod rm) {\n        allMethods.computeIfAbsent(getEndpointClassifier(rm, path), key -> new ArrayList<>())\n                .addAll(getEndpointConfigs(path, info, rm));\n    }\n\n    private String getEndpointClassifier(ResourceMethod resourceMethod, String path) {\n        String fullPath = (path.equals(\"/\") ? \"\" : path) + resourceMethod.getPath();\n        return resourceMethod.getHttpMethod() + \" \" + normalizePathParamNames(fullPath);\n    }\n\n    /**\n     * Replaces path parameter names with a fixed placeholder so that structurally equivalent","sourceCodeStart":1668,"sourceCodeEnd":1704,"githubUrl":"https://github.com/quarkusio/quarkus/blob/e1c734241f34c7919086ceb4c9262b4a58f6de44/extensions/resteasy-reactive/rest/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ResteasyReactiveProcessor.java#L1668-L1704","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\n@Path(\"/pets\")\nclass A { @GET @Path(\"{id}\") Pet get(long id) {...} }\nclass B { @GET @Path(\"/pets/{id}\") Pet find(long id) {...} }\n// after\nclass B { @GET @Path(\"/pets/details/{id}\") Pet find(long id) {...} }","handlingStrategy":"validation","validationCode":"// pre-build scan for duplicate method+path declarations\nMap<String, List<String>> seen = new HashMap<>();\nfor (Class<?> resource : resourceClasses) {\n    String basePath = resource.getAnnotation(jakarta.ws.rs.Path.class).value();\n    for (Method m : resource.getDeclaredMethods()) {\n        jakarta.ws.rs.Path p = m.getAnnotation(jakarta.ws.rs.Path.class);\n        for (String verb : List.of(\"GET\",\"POST\",\"PUT\",\"DELETE\")) {\n            try {\n                if (m.isAnnotationPresent((Class) Class.forName(\"jakarta.ws.rs.\" + verb))) {\n                    String key = verb + \" \" + basePath + (p == null ? \"\" : p.value());\n                    seen.computeIfAbsent(key, k -> new ArrayList<>()).add(resource.getName() + \"#\" + m.getName());\n                }\n            } catch (ClassNotFoundException ignored) {}\n        }\n    }\n}\nseen.entrySet().stream().filter(e -> e.getValue().size() > 1)\n    .forEach(e -> System.err.println(\"Duplicate endpoint: \" + e.getKey() + \" -> \" + e.getValue()));","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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"],"tags":["routing","jax-rs","resteasy-reactive","duplicate-endpoint","configuration"],"backgroundTag":"duplicate-endpoint-path","analyzedSha":"e1c734241f34c7919086ceb4c9262b4a58f6de44","analyzedAt":"2026-09-05T17:01:29.979Z","contentChangedAt":"2026-09-05T17:01:29.979Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}