Spring Boot
Validation Annotations
@Valid
@NotBlank
Troubleshooting

Spring boot validation annotations Valid and NotBlank not working

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

The most common reason @Valid and @NotBlank do nothing in a Spring Boot application is a missing dependency. Starting with Spring Boot 2.3, the spring-boot-starter-validation dependency is no longer included transitively through spring-boot-starter-web. You must add it explicitly:

xml
1<dependency>
2    <groupId>org.springframework.boot</groupId>
3    <artifactId>spring-boot-starter-validation</artifactId>
4</dependency>

If the dependency is already present and validation still does not trigger, the problem is almost certainly one of the five other causes covered in this article: missing @Valid on the controller parameter, using the wrong import package, placing annotations on getters instead of fields, omitting @Validated on service classes, or a misconfigured MethodValidationPostProcessor. Each cause has a specific fix.

Cause 1: Missing spring-boot-starter-validation Dependency

Before Spring Boot 2.3, spring-boot-starter-web pulled in Hibernate Validator transitively through spring-boot-starter-web -> spring-boot-starter-tomcat -> hibernate-validator. That transitive path was removed in 2.3 to reduce unnecessary dependencies.

Maven

xml
1<dependency>
2    <groupId>org.springframework.boot</groupId>
3    <artifactId>spring-boot-starter-validation</artifactId>
4</dependency>

Gradle

gradle
implementation 'org.springframework.boot:spring-boot-starter-validation'

After adding this, verify the dependency is resolved:

bash
1# Maven
2mvn dependency:tree | grep validation
3
4# Gradle
5./gradlew dependencies | grep validation

You should see hibernate-validator in the tree. If it is missing, validation annotations compile but have no runtime effect.

Cause 2: Missing @Valid on the Controller Method Parameter

The @NotBlank annotation on a field only defines a constraint. It does not trigger validation by itself. You must tell Spring to validate the incoming object by annotating the method parameter with @Valid:

java
1@RestController
2@RequestMapping("/api/users")
3public class UserController {
4
5    // WRONG: validation will not trigger
6    @PostMapping
7    public ResponseEntity<User> createUser(@RequestBody UserRequest request) {
8        // @NotBlank on fields is ignored
9        return ResponseEntity.ok(userService.create(request));
10    }
11
12    // CORRECT: @Valid triggers bean validation
13    @PostMapping
14    public ResponseEntity<User> createUser(@Valid @RequestBody UserRequest request) {
15        // @NotBlank is now enforced
16        return ResponseEntity.ok(userService.create(request));
17    }
18}
java
1public class UserRequest {
2    @NotBlank(message = "Name is required")
3    private String name;
4
5    @Email(message = "Email must be valid")
6    @NotBlank(message = "Email is required")
7    private String email;
8
9    // getters and setters
10}

Without @Valid, Spring deserializes the request body but skips validation entirely. This is the single most common cause after the dependency issue.

Cause 3: Wrong Import Package (javax vs jakarta)

Spring Boot 3.x migrated from Java EE (javax.*) to Jakarta EE (jakarta.*). Using the wrong package means annotations are present in the bytecode but the validator does not recognize them.

Spring Boot VersionCorrect Import
2.xjavax.validation.constraints.NotBlank
3.xjakarta.validation.constraints.NotBlank
java
1// Spring Boot 2.x
2import javax.validation.Valid;
3import javax.validation.constraints.NotBlank;
4
5// Spring Boot 3.x
6import jakarta.validation.Valid;
7import jakarta.validation.constraints.NotBlank;

If your IDE auto-imported the wrong package, validation compiles without errors but silently does nothing at runtime. Check every file that uses validation annotations.

Cause 4: Annotations on Getters Instead of Fields

Hibernate Validator's default access strategy validates fields, not getter methods. If your project uses field access (which is the default), placing @NotBlank on a getter will be ignored:

java
1public class UserRequest {
2    private String name;
3
4    // WRONG: validator checks fields by default, not getters
5    @NotBlank
6    public String getName() {
7        return name;
8    }
9
10    // CORRECT: annotate the field directly
11    @NotBlank
12    private String name;
13}

You can change the access strategy to PROPERTY using @AccessType, but that adds complexity. The simpler and more common convention is to annotate fields.

If you use Lombok's @Data or @Getter, annotations belong on the field, which Lombok leaves untouched when generating getters.

Cause 5: Missing @Validated on Service-Layer Classes

@Valid works automatically in Spring MVC controllers because Spring's RequestResponseBodyMethodProcessor handles it. In service classes, @Valid on method parameters does nothing unless the class is annotated with @Validated:

java
1import org.springframework.validation.annotation.Validated;
2import jakarta.validation.Valid;
3import jakarta.validation.constraints.NotBlank;
4
5@Service
6@Validated  // Required for method-level validation in services
7public class UserService {
8
9    public User createUser(@Valid UserRequest request) {
10        // Validation now triggers via AOP proxy
11        return userRepository.save(mapToEntity(request));
12    }
13
14    public User findByName(@NotBlank String name) {
15        // @NotBlank on a simple parameter also works with @Validated
16        return userRepository.findByName(name);
17    }
18}

Without @Validated, the MethodValidationPostProcessor does not create a validation proxy around the service bean. The annotations exist but are never evaluated.

Cause 6: Nested Object Validation

When a request body contains nested objects, constraints on the nested object's fields are not validated unless the field is annotated with @Valid:

java
1public class OrderRequest {
2    @NotBlank(message = "Order ID is required")
3    private String orderId;
4
5    @Valid  // Without this, Address constraints are ignored
6    @NotNull(message = "Shipping address is required")
7    private Address shippingAddress;
8}
9
10public class Address {
11    @NotBlank(message = "Street is required")
12    private String street;
13
14    @NotBlank(message = "City is required")
15    private String city;
16
17    @Pattern(regexp = "\\d{5}", message = "Zip code must be 5 digits")
18    private String zipCode;
19}

The @Valid on the shippingAddress field tells the validator to descend into the Address object and evaluate its constraints. This applies to collections as well:

java
@Valid
@NotEmpty(message = "At least one item is required")
private List<OrderItem> items;

Each OrderItem in the list will be individually validated.

Handling Validation Errors

When validation fails, Spring throws a MethodArgumentNotValidException for @RequestBody parameters. Without a handler, this returns a generic 400 response. Add an exception handler for clean error responses:

java
1@RestControllerAdvice
2public class ValidationExceptionHandler {
3
4    @ExceptionHandler(MethodArgumentNotValidException.class)
5    public ResponseEntity<Map<String, String>> handleValidationErrors(
6            MethodArgumentNotValidException ex) {
7        Map<String, String> errors = new HashMap<>();
8        ex.getBindingResult().getFieldErrors().forEach(error ->
9            errors.put(error.getField(), error.getDefaultMessage())
10        );
11        return ResponseEntity.badRequest().body(errors);
12    }
13
14    @ExceptionHandler(ConstraintViolationException.class)
15    public ResponseEntity<Map<String, String>> handleConstraintViolation(
16            ConstraintViolationException ex) {
17        Map<String, String> errors = new HashMap<>();
18        ex.getConstraintViolations().forEach(violation ->
19            errors.put(violation.getPropertyPath().toString(), violation.getMessage())
20        );
21        return ResponseEntity.badRequest().body(errors);
22    }
23}

MethodArgumentNotValidException is thrown for @Valid @RequestBody failures. ConstraintViolationException is thrown for @Validated service-layer and path/query parameter failures.

Debugging Checklist

When validation is not working, walk through this checklist in order:

StepCheckCommand/Action
1Dependency presentmvn dependency:tree | grep hibernate-validator
2@Valid on controller parameterInspect controller method signature
3Correct import packagejavax.* for Boot 2.x, jakarta.* for Boot 3.x
4Annotations on fields, not gettersInspect DTO/request classes
5@Validated on service classesInspect service class annotations
6@Valid on nested objectsInspect nested DTO fields
7Enable debug loggingSet logging.level.org.hibernate.validator=DEBUG

Enabling Debug Logging

Add these properties to see exactly what the validator is doing:

properties
logging.level.org.springframework.validation=DEBUG
logging.level.org.hibernate.validator=DEBUG

This logs which constraints are evaluated, which objects are validated, and why specific validations pass or fail.

Common Pitfalls

Upgrading Spring Boot without adding the validation starter. This is the number one cause. Projects that worked fine on 2.2 silently lose validation after upgrading to 2.3+ because the transitive dependency was removed.

Mixing javax and jakarta imports. In a codebase migrating to Spring Boot 3, some files may still use javax imports. The code compiles, but constraints from the wrong package are invisible to the validator.

Using @Valid and @Validated interchangeably. They are not identical. @Valid (from the Bean Validation spec) triggers recursive validation but does not support groups. @Validated (Spring-specific) supports groups and is required for method-level validation in services.

Forgetting @Valid on nested objects. This is especially easy to miss in deeply nested request structures. Each level of nesting requires its own @Valid annotation.

Relying on @NotNull when @NotBlank is needed. @NotNull passes for empty strings (""). @NotBlank rejects both null and blank/whitespace-only strings. For user-facing text fields, @NotBlank is almost always the right choice.

Not handling validation exceptions. Without a @RestControllerAdvice handler, validation failures produce a generic 400 response with no useful detail. Always add a handler that maps field errors to a structured response.

Summary

  • The most common fix is adding spring-boot-starter-validation to your dependencies (required since Spring Boot 2.3).
  • @Valid must appear on the controller method parameter to trigger validation, not just on the DTO fields.
  • Use javax.validation imports for Spring Boot 2.x and jakarta.validation for 3.x.
  • Place constraint annotations on fields, not getters.
  • Add @Validated to service classes for method-level validation outside controllers.
  • Annotate nested object fields with @Valid for recursive validation.
  • Always add an exception handler for MethodArgumentNotValidException to return useful error responses.

Related reading
Course
Intermediate
27 lessons
14 hours
OOD Fundamentals

Master object-oriented design from first principles, SOLID, design patterns, and classic interview problems with hands-on coding.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.