Spring Boot 4 and Jackson 3: the silent catch problem
By Nihar Ranjan Das · Fri Aug 21 2026 · 6 min read · 0 views
View as a Web StorySoftware#migration#spring boot#java#jackson#backend

Spring Boot 4 and Jackson 3: the silent catch problem
Most of the Spring Boot 4 migration announces itself. Packages move, classes get renamed, and the compiler tells you. One change does not, and it is the one worth reading first.
In Jackson 2, JsonProcessingException extended IOException. In Jackson 3, JacksonException extends RuntimeException. So a catch (IOException e) block that used to absorb JSON parse failures stops catching them. Nothing fails to compile. Nothing warns. The error just travels somewhere else, usually to a generic 500 handler.
Why this migration is urgent now
Spring Boot is the application framework most Java services are built on, and its open-source support windows are short. Spring Boot 3.5 lost open-source support in June 2026. The final free release was 3.5.16 on June 25, 2026, and every 3.x branch is now out of open-source support. Paid enterprise support for 3.5 runs to June 2032.
That matters more than any feature list. Once a branch closes, new CVEs in that line get no community patch on Maven Central. Teams that stayed on 3.5 for stability now carry unpatched risk instead. That is the opposite of what the choice was meant to buy.
So the question is no longer whether to move to Spring Boot 4. It is what order to do it in.
What actually changed with Jackson
Jackson is the JSON serialization library Spring Boot uses by default. Spring Boot 4 prefers Jackson 3, and the official migration guide lists the moves.
| Change | Jackson 2 | Jackson 3 |
|---|---|---|
| Group and package | com.fasterxml.jackson |
tools.jackson |
| Base exception | JsonProcessingException extends IOException |
JacksonException extends RuntimeException |
| Serializer base | JsonObjectSerializer |
ObjectValueSerializer |
| Builder customizer | Jackson2ObjectMapperBuilderCustomizer |
JsonMapperBuilderCustomizer |
| Component annotation | @JsonComponent |
@JacksonComponent |
| Read and write properties | spring.jackson.read.* |
spring.jackson.json.read.* |
One carve-out is worth memorising. jackson-annotations stays under com.fasterxml.jackson.core, per the same guide. That single exception breaks a lot of blind find-and-replace runs.
The catch block that stops working
Here is the pattern that breaks. It is common in file and stream handling, where JSON parsing sits inside code that already deals with IOException.
try {
Report report = objectMapper.readValue(inputStream, Report.class);
return ResponseEntity.ok(report);
} catch (IOException e) {
// Under Jackson 2 this caught malformed JSON too.
return ResponseEntity.badRequest().body("Invalid report payload");
}
Under Jackson 2 that returned a clean 400 for malformed JSON. Under Jackson 3 the parse failure is a RuntimeException. It goes straight past this block. The user gets a 500. Your handler still looks correct in code review.
Find the affected sites before you upgrade, not after:
Advertisement
rg -n "catch\s*\(\s*IOException" --type java
Then check each hit for a Jackson call inside the try. For example, a controller reading a multipart upload usually has one. A Kafka consumer that parses payloads by hand almost always does.
The fix is explicit rather than clever:
} catch (JacksonException e) {
return ResponseEntity.badRequest().body("Invalid report payload");
} catch (IOException e) {
return ResponseEntity.status(500).body("Read failed");
}
What else changes behaviour without failing the build
Serialization output shifts too. That lands in your API contract, not your compiler. Reports from teams already through the Spring Boot 4 migration rank unexpected output second only to package errors. Custom date formats, null handling and BigDecimal output all behave differently under Jackson 3 defaults.
Three checks catch most of it.
- Snapshot your JSON before the upgrade. Record the exact response body for your ten busiest endpoints. Diff after. This is the whole methodology, and it takes an hour.
- Look hard at
BigDecimal. Money fields are where a changed number format becomes a customer-visible defect, such as a price rendering as19.90in one release and19.9in the next. - Re-test null handling. Clients that treat a missing field differently from a null field will notice before you do.
Can you stay on Jackson 2 for now?
Yes, and it is a fair staging step. Spring Boot 4 ships a deprecated spring-boot-jackson2 module. It also adds a spring.jackson.use-jackson2-defaults property, which aligns the auto-configured mapper with Jackson 2 behaviour. Both are documented in the Spring Boot 4.0 migration guide.
Use it to split one risky release into two boring ones. Move to Spring Boot 4 with Jackson 2 defaults first, confirm the service is stable, then migrate the JSON layer as its own change.
Two caveats apply. The module is deprecated on arrival, so the bridge has a posted expiry. And Spring's Jackson 3 announcement notes real limits when other libraries expect one version. Mixing versions is a scheduling tool, not a destination.
Should you upgrade now, wait, or split it?
Split it, and start now. The end-of-life date has already passed, so waiting is no longer a low-risk option.
A workable order for a service in production looks like this.
- Upgrade to Spring Boot 4 with Jackson 2 defaults. Set
spring.jackson.use-jackson2-defaultsto true and ship. Nothing about your JSON should change. - Fix the exception handling. Add
JacksonExceptioncatches ahead ofIOExceptioncatches, before Jackson 3 defaults are live. - Switch to Jackson 3 defaults. Diff your snapshotted JSON responses. Fix the format differences you find.
- Remove the bridge. Drop
spring-boot-jackson2once nothing depends on it, because it will not be supported indefinitely.
Teams following the published Spring Boot 3 to 4 upgrade playbook run the same order. The reason is simple. Each step has one failure mode, so a break points at one change.
What this costs, honestly
For a small service with clean JSON handling, this is a day of work. For a large codebase with hand-rolled serializers, custom @JsonComponent classes and Kafka parsing, budget a sprint. Expect the exception-handling audit to be the slowest part.
The shape will feel familiar to anyone who has done a Pydantic AI v2 migration: the change that throws no error is the one that costs the weekend. It is also why a TypeScript 7 upgrade, where the compiler shouts at you, is the easier kind of migration to plan.
The cost of skipping it is a closed support branch. That is what should set the schedule. The migration effort is finite. The next unpatched CVE is not.
Advertisement
FAQ
Does Jackson 3 break existing catch blocks in Spring Boot 4?
Yes, silently. Jackson 2's `JsonProcessingException` extended `IOException`, while Jackson 3's `JacksonException` extends `RuntimeException`. Existing `catch (IOException e)` blocks still compile, but no longer catch JSON parse failures, so malformed input reaches your generic error handler instead.
What package do Jackson 3 classes use in Spring Boot 4?
Jackson 3 moves from `com.fasterxml.jackson` to `tools.jackson`. The exception is `jackson-annotations`, which stays under `com.fasterxml.jackson.core`. Custom serializers, deserializers and any code importing Jackson internals all need the new import paths.
Can I use Jackson 2 with Spring Boot 4?
Yes. Spring Boot 4 provides a deprecated `spring-boot-jackson2` module and the `spring.jackson.use-jackson2-defaults` property, which aligns the auto-configured mapper with Jackson 2 behaviour. Treat it as a temporary bridge, since the module is deprecated from the start.
When did Spring Boot 3.5 reach end of life?
Spring Boot 3.5 lost open-source support in June 2026, and the final free release, 3.5.16, shipped on June 25, 2026. Every Spring Boot 3.x branch is now outside open-source support, so community security patches for those lines no longer arrive on Maven Central. Commercial enterprise support for 3.5 continues until June 2032.
Comments
Loading…
Sign in to join the conversation.
Related posts

Compose 1.12 wants compileSdk 37. Which AGP is right?
Bump to Compose BOM 2026.08.00 and the build stops. The message points at compileSdk, the fix looks obvious, and then the obvious fix does not always work. The reason is that Google's own
Fri Aug 21 2026 · 5 min read · 0 views

Next.js security release lands August 26. Prep now
Vercel has told everyone that a critical bug exists in Next.js, and has not yet said what it is. The Next.js security release lands on August 26, 2026. It fixes one critical-severity vulnerability,
Fri Aug 21 2026 · 6 min read · 0 views

Pydantic AI v2 migration: the change that throws no error
The riskiest Pydantic AI v2 migration change raises no exception: openai: model names now hit the Responses API. Here is the safe upgrade path.
Thu Aug 20 2026 · 5 min read · 0 views