Why does spring-boot-3 give javax.servlet.http.HttpServletRequest ClassNotFoundException
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
If Spring Boot 3 throws ClassNotFoundException for javax.servlet.http.HttpServletRequest, the root cause is almost always the Jakarta migration. Spring Boot 3 is built on Spring Framework 6, which moved from the old javax.servlet namespace to jakarta.servlet.
The Real Breaking Change
Before Spring Boot 3, servlet APIs commonly came from packages such as javax.servlet.http. Starting with Jakarta EE 9, those packages were renamed to jakarta.servlet.http. The classes are conceptually the same, but the package names are different, so old bytecode referencing javax.servlet.http.HttpServletRequest can no longer resolve in a Boot 3 application.
That means any of the following can trigger the exception:
- your own imports still use
javax.servlet.* - a filter, interceptor, or controller library was compiled against the old servlet API
- a third-party starter pulls in pre-Jakarta dependencies
- generated code or copied examples still reference
javax
Fixing Your Own Code
The first thing to check is your imports. In Boot 3 code, controller and filter classes must import Jakarta servlet types.
If the same class still imports javax.servlet.http.HttpServletRequest, it will compile only if old dependencies are present, and that usually means your project is half-migrated.
Checking Dependencies
Even after fixing your own imports, the error can remain if an older library still depends on javax.servlet. That is common with authentication filters, servlet listeners, and older Spring extensions.
In Maven, inspect the dependency tree:
In Gradle:
You are looking for outdated libraries that still bring in javax.servlet-api or older Spring components. In a Boot 3 app, the servlet world should be aligned around Jakarta packages.
If you declare the servlet API yourself, it should also be Jakarta-based:
In many Spring Boot applications you do not need to add this dependency directly, because Boot starters manage it for you. Adding both old and new servlet APIs is a reliable way to create confusion.
Why It Often Appears During Migration
This error frequently shows up during upgrades from Spring Boot 2.x to 3.x because the rest of the codebase may look compatible at first. Controllers, services, and repository code often migrate cleanly, while servlet-related imports fail later or only at runtime through reflective loading.
A typical example is a filter that still uses old imports:
Only one old javax import is enough to break the classpath contract.
Common Pitfalls
One common mistake is updating source imports but leaving an incompatible third-party dependency in place. If a library was compiled against javax.servlet, changing only your own code is not enough. You need a Jakarta-compatible version of that library.
Another issue is forcing both javax.servlet-api and jakarta.servlet-api into the same application. That does not create compatibility. It usually creates classpath ambiguity and hides the real problem.
Developers also sometimes see this exception and assume Tomcat is missing. In most Boot 3 applications the embedded server is not the problem. The real issue is that application or library bytecode references the wrong package.
Finally, be careful with documentation written for Boot 2.x. Security filters, custom servlet components, and container customizers often need import changes plus dependency upgrades.
Summary
- Spring Boot 3 uses Jakarta packages, not
javax.servlet. - Replace
javax.servlet.http.HttpServletRequestwithjakarta.servlet.http.HttpServletRequestin your code. - Audit dependencies for old libraries compiled against
javax.servlet. - Do not mix
javax.servlet-apiandjakarta.servlet-apiin the same Boot 3 application. - If the error remains after import changes, the remaining problem is usually an outdated transitive dependency.

