{"record":{"id":"79e4d86fc1bf4775","repo":"apache/pulsar","slug":"additional-servlet-instance-of-type-classname-d","errorCode":null,"errorMessage":"Additional servlet instance of type ${className} doesn't implement the servlet interface required by the declared servlet type ${servletType}","messagePattern":"Additional servlet instance of type (.+?) doesn't implement the servlet interface required by the declared servlet type (.+?)","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"pulsar-broker-common/src/main/java/org/apache/pulsar/broker/web/plugin/servlet/AdditionalServletUtils.java","lineNumber":190,"sourceCode":"     *         by the {@link AdditionalServlet#getServletType() servlet type} the additional servlet declares\n     */\n    public Servlet toJakartaServlet(AdditionalServlet additionalServlet) {\n        AdditionalServletType servletType = additionalServlet.getServletType();\n        Object servletInstance = additionalServlet.getServletInstance();\n        switch (servletType) {\n            case JAVAX_SERVLET -> {\n                if (servletInstance instanceof javax.servlet.Servlet javaxServlet) {\n                    return new ServletWrapper(javaxServlet);\n                }\n            }\n            case JAKARTA_SERVLET -> {\n                if (servletInstance instanceof Servlet jakartaServlet) {\n                    return jakartaServlet;\n                }\n            }\n            default -> throw new IllegalArgumentException(\"Unsupported additional servlet type \" + servletType);\n        }\n        throw new IllegalArgumentException(\"Additional servlet instance of type \"\n                + (servletInstance == null ? \"null\" : servletInstance.getClass().getName())\n                + \" doesn't implement the servlet interface required by the declared servlet type \" + servletType);\n    }\n\n    private void rethrowIOException(Throwable cause)\n            throws IOException {\n        if (cause instanceof IOException) {\n            throw (IOException) cause;\n        } else if (cause instanceof RuntimeException) {\n            throw (RuntimeException) cause;\n        } else if (cause instanceof Error) {\n            throw (Error) cause;\n        } else {\n            throw new IOException(cause.getMessage(), cause);\n        }\n    }\n}\n","sourceCodeStart":172,"sourceCodeEnd":208,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/web/plugin/servlet/AdditionalServletUtils.java#L172-L208","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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"],"exampleFix":"// before (pom.xml of plugin)\n<dependency>\n  <groupId>javax.servlet</groupId>\n  <artifactId>javax.servlet-api</artifactId>\n</dependency>\n// after\n<dependency>\n  <groupId>jakarta.servlet</groupId>\n  <artifactId>jakarta.servlet-api</artifactId>\n</dependency>","handlingStrategy":"validation","validationCode":"if (!(instance instanceof jakarta.servlet.Servlet)) {\n    throw new IllegalStateException(\"Plugin servlet \" + instance.getClass().getName()\n        + \" does not implement jakarta.servlet.Servlet; rebuild the NAR against the jakarta API\");\n}","typeGuard":"static boolean isJakartaServlet(Object o) {\n    return o instanceof jakarta.servlet.Servlet;\n}","tryCatchPattern":"try {\n    jakarta.servlet.Servlet s = AdditionalServletUtils.toJakartaServlet(instance, servletType);\n} catch (IllegalArgumentException e) {\n    log.error(\"Servlet {} does not match declared type {}\", instance.getClass(), servletType, e);\n}","preventionTips":["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"],"tags":["servlet","plugin","classloader","configuration"],"backgroundTag":"servlet-interface-mismatch","analyzedSha":"820761864ed8e2a7d2e52dd9763ad2ae117c1395","analyzedAt":"2026-09-06T00:14:20.138Z","contentChangedAt":"2026-09-06T00:14:20.138Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}