Repository navigation
Adopt 422 Unprocessable Entity for payload validation errors #319
Description
Activity
- addedenhancementNew feature or requestNew feature or requestjavaPull requests that update Java codePull requests that update Java codeplanningEnables automatic issue planning with CodeRabbitEnables automatic issue planning with CodeRabbitpriority:lowNice-to-have improvement. Can be deferred without blocking other work.Nice-to-have improvement. Can be deferred without blocking other work.and removedplanningEnables automatic issue planning with CodeRabbitEnables automatic issue planning with CodeRabbit
on Apr 12, 2026 @coderabbitai plan
Reacted by coderabbitaicoderabbitai commented
on Apr 14, 2026 coderabbitaiboton Apr 14, 2026 – with coderabbitaiMore actionsCoding Plan
Summary
- Introduce a minimal
@ControllerAdvicethat only handlesMethodArgumentNotValidException, returning 422 while letting Spring's default handlers preserve 400 for malformed requests - Update the single explicit
HttpStatus.BAD_REQUESTin the controller toUNPROCESSABLE_ENTITYfor consistency - Align OpenAPI annotations and tests with the new status code without changing response body structure or adding new abstractions
Implementation Steps
Phase 1: Create Global Exception Handler for Validation Errors
Introduce a
@ControllerAdviceto interceptMethodArgumentNotValidException(thrown when@Validfails) and return 422 instead of Spring's default 400. This cleanly separates validation failures from malformed request errors.Create GlobalExceptionHandler Class
Create a centralized exception handler to customize the response for validation failures while preserving Spring's default handling for other error types.
- Create
GlobalExceptionHandler.javain thecontrollerspackage (following the existing convention of keeping HTTP-layer concerns together) - Annotate the class with
@ControllerAdviceand@RestController - Add an
@ExceptionHandlermethod forMethodArgumentNotValidExceptionthat returnsResponseEntitywithHttpStatus.UNPROCESSABLE_ENTITY(422) - Keep the response body empty (
.build()) to match the existing controller pattern—no structured error response is required by the ticket - Do NOT add handlers for
HttpMessageNotReadableExceptionor other exceptions—let Spring's defaults handle those to preserve 400 for malformed JSON
🤖 Prompt for AI agents
Create a new `GlobalExceptionHandler.java` file in the `controllers` package to handle validation exceptions globally and return HTTP 422 instead of Spring's default 400. - Annotate the class with both `@ControllerAdvice` and `@RestController` - Add a single `@ExceptionHandler` method targeting `MethodArgumentNotValidException` - The method should return a `ResponseEntity` built with `HttpStatus.UNPROCESSABLE_ENTITY` (422) and an empty body (`.build()`) - Do NOT add handlers for any other exception types (e.g., `HttpMessageNotReadableException`); leave all other error handling to Spring's defaults so that 400 is preserved for malformed JSON and other request errorsPhase 2: Update Controller and OpenAPI Annotations
Modify the controller to return 422 for the explicit squad number mismatch validation and update the OpenAPI documentation to reflect the new status code for all validation errors.
Task 1: Update Explicit Status Code in PUT Endpoint
Change the squad number mismatch check from 400 to 422 since this is a business validation failure, not a malformed request.
- In
src/main/java/.../controllers/PlayersController.java, locate the PUT endpoint's squad number mismatch check (around line 214) - Replace
HttpStatus.BAD_REQUESTwithHttpStatus.UNPROCESSABLE_ENTITY
Task 2: Update `@ApiResponse` Annotations
Update the OpenAPI documentation to declare 422 for validation failures while keeping 400 reserved for malformed requests.
- In
PlayersController.java, update the POST endpoint's@ApiResponseannotation: changeresponseCode = "400"toresponseCode = "422"and update description to"Unprocessable Entity - Validation failure" - Update the PUT endpoint's
@ApiResponseannotation similarly: change 400 to 422 with description"Unprocessable Entity - Validation failure" - Keep the existing annotation structure and
@Contentpatterns consistent with other responses
🤖 Prompt for AI agents
Update `PlayersController.java` to replace all validation-related HTTP 400 usages with HTTP 422, including both runtime logic and OpenAPI annotations. **Task 1 – Fix explicit status code in the PUT endpoint:** - Locate the squad number mismatch validation check in the PUT endpoint (around line 214) - Replace `HttpStatus.BAD_REQUEST` with `HttpStatus.UNPROCESSABLE_ENTITY` **Task 2 – Update `@ApiResponse` annotations:** - On the POST endpoint, find the `@ApiResponse` with `responseCode = "400"` and update it to `responseCode = "422"` with description `"Unprocessable Entity - Validation failure"` - Apply the same change to the PUT endpoint's corresponding `@ApiResponse` annotation - Preserve all existing `@Content` patterns and the overall annotation structure; only the response code value and description string should changePhase 3: Update Tests and Documentation
Align tests with the new 422 status code and document the change in the changelog.
Task 1: Update Test Assertions
Modify all test methods that assert 400 for validation failures to assert 422 instead.
- In
src/test/java/.../controllers/PlayersControllerTests.java, update four test methods:givenInvalidPlayer_whenPost_thenReturnsBadRequest→ change assertion toHttpStatus.UNPROCESSABLE_ENTITY.value()and consider renaming to...thenReturnsUnprocessableEntitygivenInvalidPlayer_whenPut_thenReturnsBadRequest→ same changesgivenSquadNumberMismatch_whenPut_thenReturnsBadRequest→ same changesgivenNullBodySquadNumber_whenPut_thenReturnsBadRequest→ same changes
- Rename test methods to reflect the new expected status (replace
BadRequestwithUnprocessableEntityin method names)
Task 2: Update CHANGELOG.md
Document the semantic change following the existing changelog format.
- Add entry to the
[Unreleased]section under### Changed - Follow the established bullet point format: describe that validation failures now return 422 Unprocessable Entity instead of 400 Bad Request
- Reference the ticket/issue number if applicable
🤖 Prompt for AI agents
Update the test file and changelog to reflect the new HTTP 422 status code for validation failures. **Task 1 – Update `PlayersControllerTests.java`:** - Locate the following four test methods and apply changes to each: - `givenInvalidPlayer_whenPost_thenReturnsBadRequest` - `givenInvalidPlayer_whenPut_thenReturnsBadRequest` - `givenSquadNumberMismatch_whenPut_thenReturnsBadRequest` - `givenNullBodySquadNumber_whenPut_thenReturnsBadRequest` - In each method, change the status assertion from `HttpStatus.BAD_REQUEST.value()` to `HttpStatus.UNPROCESSABLE_ENTITY.value()` - Rename each method by replacing `BadRequest` with `UnprocessableEntity` in the method name **Task 2 – Update `CHANGELOG.md`:** - In the `[Unreleased]` section, add or append a `### Changed` subsection - Add a bullet point noting that validation failures now return 422 Unprocessable Entity instead of 400 Bad Request - Follow the existing bullet point format; include a ticket/issue reference if one is availableResearch
This is a Spring Boot REST API for managing football players. The single
PlayersControllerhandles CRUD operations with@Validannotations on POST and PUT endpoints that trigger Bean Validation (JSR-380) onPlayerDTO. Currently, there is no@ControllerAdvice—Spring Boot's default exception handling returns 400 for validation failures. One explicitHttpStatus.BAD_REQUESTexists in the PUT endpoint for squad number mismatch validation. Tests use MockMvc with AssertJ BDD-style assertions, with four tests currently expecting 400 for validation failures.
🚀 Next Steps
🤖 All AI agent prompts combined
Task: 1 Create a new `GlobalExceptionHandler.java` file in the `controllers` package to handle validation exceptions globally and return HTTP 422 instead of Spring's default 400. - Annotate the class with both `@ControllerAdvice` and `@RestController` - Add a single `@ExceptionHandler` method targeting `MethodArgumentNotValidException` - The method should return a `ResponseEntity` built with `HttpStatus.UNPROCESSABLE_ENTITY` (422) and an empty body (`.build()`) - Do NOT add handlers for any other exception types (e.g., `HttpMessageNotReadableException`); leave all other error handling to Spring's defaults so that 400 is preserved for malformed JSON and other request errors =============================================================================== Task: 2 Update `PlayersController.java` to replace all validation-related HTTP 400 usages with HTTP 422, including both runtime logic and OpenAPI annotations. **Task 1 – Fix explicit status code in the PUT endpoint:** - Locate the squad number mismatch validation check in the PUT endpoint (around line 214) - Replace `HttpStatus.BAD_REQUEST` with `HttpStatus.UNPROCESSABLE_ENTITY` **Task 2 – Update `@ApiResponse` annotations:** - On the POST endpoint, find the `@ApiResponse` with `responseCode = "400"` and update it to `responseCode = "422"` with description `"Unprocessable Entity - Validation failure"` - Apply the same change to the PUT endpoint's corresponding `@ApiResponse` annotation - Preserve all existing `@Content` patterns and the overall annotation structure; only the response code value and description string should change =============================================================================== Task: 3 Update the test file and changelog to reflect the new HTTP 422 status code for validation failures. **Task 1 – Update `PlayersControllerTests.java`:** - Locate the following four test methods and apply changes to each: - `givenInvalidPlayer_whenPost_thenReturnsBadRequest` - `givenInvalidPlayer_whenPut_thenReturnsBadRequest` - `givenSquadNumberMismatch_whenPut_thenReturnsBadRequest` - `givenNullBodySquadNumber_whenPut_thenReturnsBadRequest` - In each method, change the status assertion from `HttpStatus.BAD_REQUEST.value()` to `HttpStatus.UNPROCESSABLE_ENTITY.value()` - Rename each method by replacing `BadRequest` with `UnprocessableEntity` in the method name **Task 2 – Update `CHANGELOG.md`:** - In the `[Unreleased]` section, add or append a `### Changed` subsection - Add a bullet point noting that validation failures now return 422 Unprocessable Entity instead of 400 Bad Request - Follow the existing bullet point format; include a ticket/issue reference if one is available💡 Iterate on the plan with:
@coderabbitai <feedback>Example Feedback - `@coderabbitai` You can skip phase 3. Add a simple unit test case for phase 2. - `@coderabbitai` For design choice 1 go ahead with option 3 and replan.
💬 Have feedback or questions? Drop into our discord!
- Introduce a minimal
- pinned this issue
on Apr 19, 2026 - added a commit that references this issue
on Apr 19, 2026 Implemented in #323.
- unpinned this issue
on Apr 20, 2026
Problem
Field validation failures currently return
400 Bad Request, which conflates two distinct error categories:Content-Type)Proposed Solution
Return
422 Unprocessable Entityfor field validation failures and reserve400 Bad Requestfor genuinely malformed requests.Replace
HttpStatus.BAD_REQUESTwithHttpStatus.UNPROCESSABLE_ENTITYin controller branches that handle validation failures (e.g.@Validconstraint violations, squad number mismatch). Spring's defaultHttpMessageNotReadableExceptionhandler (unparseable JSON) should remain400.Acceptance Criteria
422 Unprocessable Entity400 Bad Requestis reserved for malformed requests (unparseable JSON, wrongContent-Type)@ApiResponseannotations updated to declare422instead of400for validation responsesCHANGELOG.mdupdatedReferences