apache/pulsar · error · IllegalArgumentException
Additional servlet instance of type ${className} doesn't imp
Error message
Additional servlet instance of type ${className} doesn't implement the servlet interface required by the declared servlet type ${servletType} What it means
This IllegalArgumentException is thrown by AdditionalServletUtils.toJakartaServlet when an additional servlet plugin's class does not implement the servlet interface implied by its declared servlet type (javax vs jakarta). Pulsar supports both servlet generations, and the plugin class must implement the one matching the declared type. It signals a broken or mislabeled servlet plugin package.
Source
Thrown at pulsar-broker-common/src/main/java/org/apache/pulsar/broker/web/plugin/servlet/AdditionalServletUtils.java:190
* by the {@link AdditionalServlet#getServletType() servlet type} the additional servlet declares
*/
public Servlet toJakartaServlet(AdditionalServlet additionalServlet) {
AdditionalServletType servletType = additionalServlet.getServletType();
Object servletInstance = additionalServlet.getServletInstance();
switch (servletType) {
case JAVAX_SERVLET -> {
if (servletInstance instanceof javax.servlet.Servlet javaxServlet) {
return new ServletWrapper(javaxServlet);
}
}
case JAKARTA_SERVLET -> {
if (servletInstance instanceof Servlet jakartaServlet) {
return jakartaServlet;
}
}
default -> throw new IllegalArgumentException("Unsupported additional servlet type " + servletType);
}
throw new IllegalArgumentException("Additional servlet instance of type "
+ (servletInstance == null ? "null" : servletInstance.getClass().getName())
+ " doesn't implement the servlet interface required by the declared servlet type " + servletType);
}
private void rethrowIOException(Throwable cause)
throws IOException {
if (cause instanceof IOException) {
throw (IOException) cause;
} else if (cause instanceof RuntimeException) {
throw (RuntimeException) cause;
} else if (cause instanceof Error) {
throw (Error) cause;
} else {
throw new IOException(cause.getMessage(), cause);
}
}
}
View on GitHub (pinned to 820761864e)
Solutions
- Rebuild the servlet plugin against the servlet API generation matching the declared servletType (jakarta.servlet for jakarta type)
- Fix the servlet type declared in the plugin's metadata so it matches the interface the class actually implements
- Check for classloader isolation issues — the servlet must implement the Servlet interface loaded by Pulsar's classpath, not an isolated copy
- If the plugin is third-party, obtain a version compatible with your Pulsar version's servlet API
Example fix
// before (pom.xml of plugin) <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> </dependency> // after <dependency> <groupId>jakarta.servlet</groupId> <artifactId>jakarta.servlet-api</artifactId> </dependency>
Defensive patterns
Strategy: validation
Validate before calling
if (!(instance instanceof jakarta.servlet.Servlet)) {
throw new IllegalStateException("Plugin servlet " + instance.getClass().getName()
+ " does not implement jakarta.servlet.Servlet; rebuild the NAR against the jakarta API");
} Type guard
static boolean isJakartaServlet(Object o) {
return o instanceof jakarta.servlet.Servlet;
} Try / catch
try {
jakarta.servlet.Servlet s = AdditionalServletUtils.toJakartaServlet(instance, servletType);
} catch (IllegalArgumentException e) {
log.error("Servlet {} does not match declared type {}", instance.getClass(), servletType, e);
} Prevention
- Build plugin NARs against the same servlet API generation your Pulsar version uses
- Declare the servletType in plugin metadata to match the interface actually implemented
- Test plugin loading with the target Pulsar version before upgrading production
When it happens
Trigger: Calling toJakartaServlet (via AdditionalServletUtils.load) with a servlet instance that passes instantiation but fails the instanceof check for the declared servletType (neither javax.servlet.Servlet nor jakarta.servlet.Servlet as required), or when the declared servletType is not one of the supported values (which throws the related 'Unsupported additional servlet type' variant).
Common situations: A NAR was built against javax.servlet APIs but declared as a jakarta servlet (or vice versa); a custom servlet plugin was upgraded to a new Pulsar version that switched servlet generations; the metadata (servlet type declaration) in the NAR is wrong; a classloading isolation issue means the servlet implements the interface from a different classloader.
Related errors
- Additional servlets `${name}` does NOT provide an additional
- No additional servlet is found for name `${servletName}`. Av
- The '${offloaderName}' offloader does not provide an offload
- Class ${factoryClass} does not implement interface ${interfa
- ${key} already exists in the dynamicConfigurationMap
AI-assisted analysis of apache/pulsar@820761864e (2026-09-06).
Data as JSON: /api/errors/79e4d86fc1bf4775.
Report an issue: GitHub.