Spring Boot 4.0 quietly changed how HttpStatus prints, and 4.1 didn't fix it, it just kept going. If your logs or tests expect "200 OK" and get "OK" instead, you're not imagining things. Here's what actually changed and what to check before it bites you in production.
I upgraded a side project to Spring Boot 4.1 last week and immediately got hit with a wall of failing tests. Not because the endpoints were broken. Not because of some security config change. It was because HttpStatus.OK.toString() no longer returns "200 OK" . It returns "OK" . That's the whole change. It still took me a solid twenty minutes to figure out why half my assertEquals calls in a contract test suite were suddenly red. My first assumption was that this landed in 4.1, since that's the version I jumped to directly from Boot 3.5.x. But something about the timing bugged me, so I went back and checked the actual release history instead of trusting my own upgrade path as evidence. Turns out I had the version wrong. The behavior change ships with Spring Boot 4.0 , the first release built on Spring Framework 7. Boot 4.1 didn't introduce anything here. It just inherited the change I ran into, same as every release after 4.0 will. That matters more than it sounds like it should, because if you're planning to jump straight from Boot 3.5.x to 4.0 (which is exactly where the ITJobOpportunities backend sits right now, on Java 25 and Spring Boot 3.5.6), you'll hit this the moment you land on 4.0. There's no grace period where 4.0 behaves like the old versions and 4.1 is where things change. It's already there on day one of the 4.x line. If you've been writing Spring apps for a while (I'm going on 25 years in Java, most of that with Spring in some form), you probably have HttpStatus.toString() baked into your muscle memory without even realizing it. Log statements, test assertions, error messages you throw back at clients. That little 200 OK or 404 NOT_FOUND string has been showing up in stack traces and console output for years. Spring Framework 7, and every Boot release built on it starting with 4.0, changed the underlying toString() implementation on the HttpStatus enum. Now you get the reason phrase alone: OK , NOT_FOUND , INTERNAL_SERVER_ERROR . No numeric code prefix. Here's the diff in plain terms: // Spring Boot 3.5.x and earlier (Spring Framework 6.x) HttpStatus.OK.toString(); // -> "200 OK" // Spring Boot 4.0 and later (Spring Framework 7+) HttpStatus.OK.toString(); // -> "OK" Small change. Big blast radius if you weren't reading the changelog closely, and an even bigger blast radius if you assumed it was scoped to a later minor version. Why this actually happened I dug into this because I wanted to know whether it was intentional or a regression, and whether it was really tied to 4.1 the way I first assumed. It's intentional, and it's part of how HttpStatusCode and HttpStatus got restructured for Spring Framework 7. The framework team wants HttpStatus to behave more like a proper enum, where toString() gives you the constant's name, and if you want the numeric code plus phrase, you call getReasonPhrase() or build the string yourself from value() and getReasonPhrase() . Once I understood the reasoning, I didn't hate it. The old toString() was doing double duty, mixing identity representation with display formatting into one method. That's not great API design. But it's the kind of thing that deserves a flashing warning in the release notes for 4.0 specifically, not a single bullet buried three sections into a Framework 7 migration guide that most people reading Boot's changelog will never open. Where this bit me I run into these small-but-everywhere changes constantly because I maintain a handful of production services for ITJobOpportunities, the job platform I founded in April 2024. The backend ( jobs-posting ) is currently on Java 25 and Spring Boot 3.5.6, so I haven't hit this in production yet. But I test-drive framework upgrades on smaller internal tools before I ever touch the main API, and one of those tools, a small admin utility for checking Easy Apply webhook deliverability, got bumped to Boot 4.1 first. Since the change actually originates at 4.0, my tool would have broken the same way if I'd stopped one minor version earlier. That tool logs response statuses for debugging when a notification fails to send. The log line looked like this: log.warn("Notification delivery failed with status {}", response.getStatusCode()); Simple enough. Except now the log output went from: Notification delivery failed with status 502 BAD_GATEWAY to: Notification delivery failed with status BAD_GATEWAY No numeric code. I had a Grafana Loki query grepping for 5\d\d patterns that had been running for months. That query just silently stopped matching anything. Nothing crashed. No exception. It quietly returned zero results, and I didn't notice until I was staring at a dashboard wondering why our error rate suddenly looked perfect for three days straight. Nothing was perfect. My query was just broken, and it would have broken the exact same way on 4.0 as it did on 4.1. The tests that broke, and why Test assertions were the loudest failure mode. I had integration tests in that same webhook utility written like this: @Test void shouldReturnOkOnSuccessfulDelivery() { ResponseEntity<Void> response = webhookClient.send(payload); assertEquals("200 OK", response.getStatusCode().toString()); } That failed after the upgrade with a message like expected: <200 OK> but was: <OK> . Not scary on its own, but multiply that across a few dozen tests written the same lazy way, and yes, that was lazy testing on my part, and you get a red CI pipeline that looks far worse than the actual problem. The fix is straightforward once you know what changed: @Test void shouldReturnOkOnSuccessfulDelivery() { ResponseEntity<Void> response = webhookClient.send(payload); assertEquals(HttpStatus.OK, response.getStatusCode()); } Compare the enum directly instead of stringifying it and comparing strings. Which, if I'm honest, is what I should have been doing the whole time. Comparing toString() output was always a code smell. It just happened to work by accident because the old string format included both pieces of information I cared about. That's the real lesson: if your tests or your logging depend on the exact formatting of a toString() method from a framework you don't control, you're one major version bump away from a surprise, and in this case it wasn't even a minor bump. It was baked into the first release of the new line. How I fixed it across the codebase For anyone dealing with this right now, on 4.0, 4.1, or planning the jump from 3.5.x, here's roughly what I did, in order: Grep for .toString() calls on HttpStatus and HttpStatusCode types across the codebase. A plain grep -rn "StatusCode.*toString\|HttpStatus.*toString" got me most of the way in about five minutes. Replace string-based log formatting with structured logging that captures the numeric code and reason phrase separately. Honestly better practice anyway if you're shipping logs somewhere you want to query by code range. Fix test assertions to compare enum values, not stringified output. No exceptions to this rule going forward. Add a helper if you genuinely need the old format for something like a legacy error response body some downstream consumer parses. This happens more than people think in integrations with older partner systems. That last point matters more than people give it credit for. If some external system is actually parsing your "404 NOT_FOUND" string, you can't just quietly change the output. You need a small utility method: public static String formatStatus(HttpStatusCode status) { return status.value() + " " + HttpStatus.resolve(status.value()).getReasonPhrase(); } Or, if you're using HttpStatus directly: public static String formatStatus(HttpStatus status) { return status.value() + " " + status.getReasonPhrase(); } Drop that into a shared utils class, use it anywhere you were relying on the old toString() behavior, and move on. The bigger pattern I keep seeing in framework upgrades This isn't really an article about one enum's toString() method, or ab...