apache/pulsar · error · IllegalArgumentException
Cannot add servlet at %s, path %s already exists
Error message
Cannot add servlet at %s, path %s already exists
What it means
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.
Source
Thrown at pulsar-proxy/src/main/java/org/apache/pulsar/proxy/server/WebServer.java:248
public ServletContextHandler addServlet(String basePath, ServletHolder servletHolder,
List<Pair<String, Object>> attributes) {
return addServlet(basePath, servletHolder, attributes, true);
}
public ServletContextHandler addServlet(String basePath, ServletHolder servletHolder,
List<Pair<String, Object>> attributes, boolean requireAuthentication) {
return addServlet(basePath, servletHolder, attributes, requireAuthentication, true);
}
private ServletContextHandler addServlet(String basePath, ServletHolder servletHolder,
List<Pair<String, Object>> attributes, boolean requireAuthentication,
boolean checkForExistingPaths) {
popularServletParams(servletHolder, config);
if (checkForExistingPaths) {
Optional<String> existingPath = servletPaths.stream().filter(p -> p.startsWith(basePath)).findFirst();
if (existingPath.isPresent()) {
throw new IllegalArgumentException(
String.format("Cannot add servlet at %s, path %s already exists", basePath,
existingPath.get()));
}
}
servletPaths.add(basePath);
ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
context.setContextPath(basePath);
context.addServlet(servletHolder, MATCH_ALL);
// Allow %2F-encoded path separators (admin paths embed encoded topic names); Jetty 12 ee10 rejects
// ambiguous URIs at the servlet layer by default (PIP-472 / Jetty 12).
context.getServletHandler().setDecodeAmbiguousURIs(true);
context.addFilter(new FilterHolder(new CustomHeaderFilter(config)), "/*", null);
for (Pair<String, Object> attribute : attributes) {
context.setAttribute(attribute.getLeft(), attribute.getRight());
}
filterInitializer.addFilters(context, requireAuthentication);View on GitHub (pinned to 820761864e)
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
Example fix
// before
webServer.addRestResource("/admin", ...
webServer.addServlet("/admin", ...); // duplicate
// after
webServer.addRestResource("/admin", ...);
webServer.addServlet("/admin/functions", ...); // unique path Defensive patterns
Strategy: validation
Validate before calling
Set<String> registeredPaths = new HashSet<>();
void safeAddServlet(WebServer ws, String path, ServletHolder holder, Map<String,String> cfg) {
if (!registeredPaths.add(path)) {
throw new IllegalArgumentException("Path " + path + " already registered");
}
ws.addServlet(path, holder, cfg);
} Type guard
boolean isFreePath(Set<String> existingPaths, String basePath) {
return existingPaths.stream().noneMatch(p -> basePath.equals(p) || p.startsWith(basePath) || basePath.startsWith(p));
} Try / catch
try {
webServer.addRestResource(path, ...);
} catch (IllegalArgumentException e) {
if (e.getMessage().contains("already exists")) {
LOG.warn("Skipping duplicate servlet registration at {}", path);
return; // or choose a different path
}
throw e;
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Additional servlets `${name}` does NOT provide an additional
- Additional servlet instance of type ${className} doesn't imp
- No additional servlet is found for name `${servletName}`. Av
- Malformed extension found for extension name `${extensionNam
- Invalid IP address filter '${ipAddressString}'
AI-assisted analysis of apache/pulsar@820761864e (2026-09-06).
Data as JSON: /api/errors/62497279f0fc7397.
Report an issue: GitHub.