🔴 The Error You're Seeing

Confirm this matches your console output. If it does, you're in the right place.

ERROR LOG// DOC-DERIVED — method varies by framework; namespace pattern is the tell: Exception in thread "main" java.lang.NoSuchMethodError: javax.servlet.http.HttpServletRequest.getHttpServletMapping()Ljavax/servlet/http/HttpServletMapping; at org.springframework.web.util.WebUtils.getServletMapping(WebUtils.java:1234) at org.springframework.web.servlet.DispatcherServlet.processRequest(DispatcherServlet.java:1020) at javax.servlet.http.HttpServlet.service(HttpServlet.java:623)

⚡ Quick Fix Works 80% of the time

Match framework generation to container generation — Spring Boot 2/javax stays on Tomcat 9; Spring Boot 3/jakarta goes to Tomcat 10+ — and never bundle servlet-api inside the war.

<!-- war deployments: container provides the API --> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>4.0.1</version> <scope>provided</scope> </dependency> # deploy matrix Spring Boot 2.7 + javax -> Tomcat 9.x Spring Boot 3.2 + jakarta -> Tomcat 10.1.x

🧠 Why this Happens

Tap to expand the deep technical explanation

Jakarta EE 9 relocated every javax.servlet class to jakarta.servlet as part of the trademark handover — same API, different package names, deliberately binary-incompatible. A Tomcat 10 container instantiates jakarta-typed request objects; a Spring 5 DispatcherServlet compiled against javax declares its parameters in javax terms. When the framework receives an object whose type lives in another namespace hierarchy, resolving the expected method fails with NoSuchMethodError; when the javax classes are simply absent, the same clash surfaces earlier as NoClassDefFoundError. getHttpServletMapping specifically arrived in Servlet 4.0, so even pure-javax stacks hit this when an older-than-4.0 container serves a newer framework.

The HITEC City Parking Spot Analogy:

Two airlines merge but keep separate boarding systems for a season. Your ticket says Gate A-12 in Terminal 2; the scanner at that gate only reads Terminal 3 boarding passes — same passenger, unreadable document format.

🔁 How to Reproduce Confirm this is your error

Build any Spring Boot 2 executable war and deploy to a stock Tomcat 10 container; first HTTP request walks the framework into javax-typed casts against jakarta objects. DOC-DERIVED — exact failing method varies by framework version.

🛠️ Solutions (5 Ways to Fix)

Solution 1✓ Most common cause

Align the whole stack on one namespace generation

👉 Use this as the durable fix: pick the row of the compatibility matrix and standardize.

Boot 2.x pairs with Tomcat 9 and javax everywhere; Boot 3+ pairs with Tomcat 10.1+ and jakarta everywhere. Mixed fleets need two deployment tracks until migration completes — one app, one matching container, no cross-bred wars.

# canonical pairings - one namespace per deployment, never mixed: # Spring Boot 2.7 line | javax -> Tomcat 9.x # Spring Boot 3.2 line | jakarta -> Tomcat 10.1+ # # executable jar? pin the embedded container to the matching train only. <properties> <tomcat.version>10.1.20</tomcat.version> <!-- Boot 3.2 line --> </properties>
Solution 2

Run the Tomcat migration tool over your jars

👉 Use this when moving a large codebase from javax to jakarta without rewriting by hand.

Apache’s JakartaEE migration tool rewrites bytecode references javax.servlet → jakarta.servlet across your jars, producing binaries the new container resolves natively while sources migrate gradually.

java -jar jakartaee-migration-1.0.0.jar \ --webapp-src legacy.war --webapp-dest migrated.war -zip
Solution 3

Mark servlet-api provided so exactly one copy ships

👉 Use this whenever the error involves duplicated servlet classes rather than missing methods.

Bundling javax.servlet-api inside WEB-INF/lib shadows the container’s implementation with a bare API jar — methods exist on paper but not on the live request objects. Provided scope lets the container win.

<dependency> <groupId>jakarta.servlet</groupId> <artifactId>jakarta.servlet-api</artifactId> <version>6.0.0</version> <scope>provided</scope> </dependency>
Solution 4

Pin embedded-container versions explicitly in Boot builds

👉 Use this when executable jars embed the wrong Tomcat/Jetty generation.

Spring Boot chooses an embedded server per release train; forcing tomcat.version against the wrong train manufactures the clash inside a single artifact. Let the BOM decide, or upgrade the train wholesale.

<properties> <!-- only override within the matching Boot generation! --> <tomcat.version>10.1.20</tomcat.version> <!-- Boot 3.2 line --> </properties>
Solution 5

Support both namespaces via reflection shims

👉 DEV ONLY — library authors bridging eras; see note.

Borderline DEV ONLY: reflective adapters that detect jakarta versus javax interfaces keep dual-mode libraries alive, but they trade type safety for stringly-typed dispatch, double maintenance, and subtle loader-order bugs. Acceptable inside widely deployed integration libraries; never in application code.

// DEV ONLY - bridge pattern for library authors String ns = Class.forName("jakarta.servlet.http.HttpServletRequest") .isInstance(req) ? "jakarta" : "javax"; // dispatch through MethodHandles on the detected namespace

📋 Version Notes

Java 8

javax-only era; Servlet 3.1/4.0 containers common.

Java 11

Tomcat 10 ships jakarta (2020); mixed fleets begin hitting this.

Java 17

Spring Boot 3 makes jakarta the default; Boot 2 EOL pressure rises.

Java 21

javax artifacts increasingly unmaintained — migration is now urgent debt.

🛡️ How to Prevent This Next Time

Document the approved framework/container matrix in the platform README, run smoke tests deploying the real artifact onto the real container version in CI, and audit dependencies for stray servlet-api bundles before release.