{"record":{"id":"62497279f0fc7397","repo":"apache/pulsar","slug":"cannot-add-servlet-at-s-path-s-already-exists","errorCode":null,"errorMessage":"Cannot add servlet at %s, path %s already exists","messagePattern":"Cannot add servlet at (.+?), path (.+?) already exists","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"pulsar-proxy/src/main/java/org/apache/pulsar/proxy/server/WebServer.java","lineNumber":248,"sourceCode":"    public ServletContextHandler addServlet(String basePath, ServletHolder servletHolder,\n                                            List<Pair<String, Object>> attributes) {\n        return addServlet(basePath, servletHolder, attributes, true);\n    }\n\n    public ServletContextHandler addServlet(String basePath, ServletHolder servletHolder,\n                           List<Pair<String, Object>> attributes, boolean requireAuthentication) {\n        return addServlet(basePath, servletHolder, attributes, requireAuthentication, true);\n    }\n\n    private ServletContextHandler addServlet(String basePath, ServletHolder servletHolder,\n                                             List<Pair<String, Object>> attributes, boolean requireAuthentication,\n                                             boolean checkForExistingPaths) {\n        popularServletParams(servletHolder, config);\n\n        if (checkForExistingPaths) {\n            Optional<String> existingPath = servletPaths.stream().filter(p -> p.startsWith(basePath)).findFirst();\n            if (existingPath.isPresent()) {\n                throw new IllegalArgumentException(\n                        String.format(\"Cannot add servlet at %s, path %s already exists\", basePath,\n                                existingPath.get()));\n            }\n        }\n        servletPaths.add(basePath);\n\n        ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);\n        context.setContextPath(basePath);\n        context.addServlet(servletHolder, MATCH_ALL);\n        // Allow %2F-encoded path separators (admin paths embed encoded topic names); Jetty 12 ee10 rejects\n        // ambiguous URIs at the servlet layer by default (PIP-472 / Jetty 12).\n        context.getServletHandler().setDecodeAmbiguousURIs(true);\n        context.addFilter(new FilterHolder(new CustomHeaderFilter(config)), \"/*\", null);\n        for (Pair<String, Object> attribute : attributes) {\n            context.setAttribute(attribute.getLeft(), attribute.getRight());\n        }\n\n        filterInitializer.addFilters(context, requireAuthentication);","sourceCodeStart":230,"sourceCodeEnd":266,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-proxy/src/main/java/org/apache/pulsar/proxy/server/WebServer.java#L230-L266","documentation":"WebServer.addServlet refuses to register a servlet (or REST resource) whose base path would overlap with an already-registered path — each base path may only be claimed once so URL routing stays unambiguous. It throws IllegalArgumentException when checkForExistingPaths is true and an existing registered path starts with (or equals) the new basePath.","triggerScenarios":"Calling webServer.addServlet(path, servletHolder, config) or webServer.addRestResource(path, ...) twice with the same basePath; adding a path that is a prefix of or equal to one already added (e.g. /admin and /admin); adding a REST resource after a servlet already claimed the same path prefix.","commonSituations":"Two extensions or integrations both trying to register /admin in the proxy web server; accidentally registering the same resource class twice during initialization; a refactor introducing a duplicate addRestResource call in a startup path.","solutions":["Remove the duplicate addServlet/addRestResource call for the same basePath","Change one of the paths so each registered base path is unique (e.g. /admin/extension instead of /admin)","If re-registration is intentional on restart, ensure the WebServer instance is recreated rather than reused with stale servletPaths","Check initialization order so only one component claims the shared path"],"exampleFix":"// before\nwebServer.addRestResource(\"/admin\", ...\nwebServer.addServlet(\"/admin\", ...); // duplicate\n\n// after\nwebServer.addRestResource(\"/admin\", ...);\nwebServer.addServlet(\"/admin/functions\", ...); // unique path","handlingStrategy":"validation","validationCode":"Set<String> registeredPaths = new HashSet<>();\nvoid safeAddServlet(WebServer ws, String path, ServletHolder holder, Map<String,String> cfg) {\n    if (!registeredPaths.add(path)) {\n        throw new IllegalArgumentException(\"Path \" + path + \" already registered\");\n    }\n    ws.addServlet(path, holder, cfg);\n}","typeGuard":"boolean isFreePath(Set<String> existingPaths, String basePath) {\n    return existingPaths.stream().noneMatch(p -> basePath.equals(p) || p.startsWith(basePath) || basePath.startsWith(p));\n}","tryCatchPattern":"try {\n    webServer.addRestResource(path, ...);\n} catch (IllegalArgumentException e) {\n    if (e.getMessage().contains(\"already exists\")) {\n        LOG.warn(\"Skipping duplicate servlet registration at {}\", path);\n        return; // or choose a different path\n    }\n    throw e;\n}","preventionTips":["Maintain a single registry of claimed web paths across extensions and core resources","Use distinct prefixes per component (e.g. /admin/extension-a) instead of a shared /admin","Guard addServlet/addRestResource calls so init code is idempotent on restart","Grep initialization code for duplicate addRestResource calls with the same path before release"],"tags":["pulsar-proxy","web-server","servlet","path-conflict","configuration"],"backgroundTag":"duplicate-path-registration","analyzedSha":"820761864ed8e2a7d2e52dd9763ad2ae117c1395","analyzedAt":"2026-09-06T00:14:20.138Z","contentChangedAt":"2026-09-06T00:14:20.138Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}