How to Fix NullPointerException in Spring Boot

Type: Software Reference Confidence: 0.93 Sources: 8 Verified: 2026-02-23 Freshness: quarterly

TL;DR

Constraints

Quick Reference

#NPE PatternLikelihoodSignatureFix
1 @Autowired field is null ~40% this.someService is null Class instantiated with new — let Spring manage the bean [src3]
2 @Autowired field null (bean exists) ~20% Bean class has no stereotype annotation Add @Component/@Service/@Repository [src1]
3 NPE in constructor ~10% NPE in <init> method Move to @PostConstruct or constructor injection [src1]
4 NPE in @Scheduled ~5% Method is static or class not a bean Ensure @Component and non-static [src2]
5 NPE accessing Optional.get() ~8% NoSuchElementException Use Optional.orElseThrow() [src5]
6 NPE in filter/interceptor ~5% Filter outside Spring context Register via FilterRegistrationBean [src6]
7 NPE in test ~8% Using @Mock without @InjectMocks Use @SpringBootTest + @MockBean [src4]
8 NPE with @Value ~4% Property not set or class not a bean Check application.properties [src2]

Decision Tree

START
├── Is the NPE on a @Autowired / injected field?
│   ├── YES → Was the object created with "new"?
│   │   ├── YES → ROOT CAUSE: Spring doesn't inject into manually-created objects [src3]
│   │   │   └── FIX: Inject the bean instead of using "new"
│   │   └── NO → Does the class have @Component/@Service/@Repository?
│   │       ├── NO → ROOT CAUSE: Missing stereotype annotation [src1]
│   │       │   └── FIX: Add @Service or @Component to the class
│   │       └── YES → Is the field accessed in the constructor?
│   │           ├── YES → ROOT CAUSE: Fields not yet injected during construction [src1]
│   │           │   └── FIX: Use constructor injection or @PostConstruct
│   │           └── NO → Is class in a package scanned by @SpringBootApplication?
│   │               ├── NO → ROOT CAUSE: Class outside component scan range [src2]
│   │               │   └── FIX: Move class or add @ComponentScan
│   │               └── YES → Check for @Autowired(required=false) or @Conditional
│   └── NO → Is it Optional.get() without isPresent check?
│       ├── YES → FIX: Use Optional.orElseThrow() [src5]
│       └── NO → Is this a GraalVM native image build?
│           ├── YES → FIX: Add @RegisterReflectionForBinding or RuntimeHints [src1]
│           └── NO → Standard Java NPE — check for null references
└── DEFAULT → logging.level.org.springframework=DEBUG

Step-by-Step Guide

1. Read the stack trace carefully

The NPE stack trace tells you exactly which field is null and where. Java 17+ provides enhanced NPE messages with the exact null reference. [src2]

java.lang.NullPointerException: Cannot invoke "com.example.UserService.findById(Long)"
because "this.userService" is null
    at com.example.UserController.getUser(UserController.java:25)

This means userService field is null at line 25 of UserController.

2. Check if the class is Spring-managed

// ❌ THIS CAUSES NPE — "new" bypasses Spring DI
UserController controller = new UserController();
controller.getUser(1L);  // userService is null!

// ✅ CORRECT — let Spring inject
@RestController  // Spring manages this
public class UserController {
    private final UserService userService;

    public UserController(UserService userService) {  // Constructor injection
        this.userService = userService;
    }
}

Verify: Check that the class has a stereotype annotation (@Component, @Service, @Repository, @Controller, @RestController, @Configuration). [src1]

3. Verify the dependency is a bean

// ❌ Missing @Service — Spring won't create this bean
public class UserService {
    public User findById(Long id) { ... }
}

// ✅ CORRECT — @Service marks it as a Spring bean [src1]
@Service
public class UserService {
    public User findById(Long id) { ... }
}

Verify: Run with --debug flag and search the auto-configuration report for the bean name.

4. Use constructor injection (recommended over field injection)

// ❌ Field injection — harder to test, NPE if constructed manually
@RestController
public class UserController {
    @Autowired
    private UserService userService;  // null if new UserController()
}

// ✅ Constructor injection — fail-fast, testable [src1]
@RestController
public class UserController {
    private final UserService userService;

    public UserController(UserService userService) {
        this.userService = userService;  // guaranteed non-null
    }
}

Verify: Application fails to start if the dependency is missing (fail-fast behavior). [src7]

5. Verify component scan coverage

// Main class in com.example
@SpringBootApplication  // Scans com.example and sub-packages
public class MyApp { ... }

// ❌ This bean is OUTSIDE the scan range
package com.other;  // NOT under com.example
@Service
public class UserService { ... }  // Will NOT be discovered

// ✅ FIX: Add explicit component scan
@SpringBootApplication
@ComponentScan(basePackages = {"com.example", "com.other"})
public class MyApp { ... }

Verify: logging.level.org.springframework=DEBUG — look for "Scanning" messages. [src2]

Code Examples

Java: Common NPE scenario and fix

Full script: java-complete-example-common-npe-scenario-and-fix.java (57 lines)

// SCENARIO: @Autowired field is null
// Input:  Controller with field injection, instantiated manually
// Output: NullPointerException at runtime

// ❌ BAD — the helper is created with "new", so Spring doesn't inject
@RestController
public class OrderController {
    @Autowired private OrderService orderService;

    @GetMapping("/orders/{id}")
    public Order getOrder(@PathVariable Long id) {
        OrderValidator validator = new OrderValidator();  // PROBLEM HERE
        validator.validate(id);  // NPE — validator.orderRepo is null
        return orderService.findById(id);
    }
}

public class OrderValidator {
    @Autowired private OrderRepository orderRepo;  // ALWAYS NULL

    public void validate(Long id) {
        orderRepo.existsById(id);  // NullPointerException!
    }
}

// ✅ GOOD — inject the validator as a bean
@Component  // Now Spring manages it
public class OrderValidator {
    private final OrderRepository orderRepo;

    public OrderValidator(OrderRepository orderRepo) {
        this.orderRepo = orderRepo;  // Guaranteed non-null
    }

    public void validate(Long id) {
        if (!orderRepo.existsById(id)) {
            throw new IllegalArgumentException("Order not found: " + id);
        }
    }
}

@RestController
public class OrderController {
    private final OrderService orderService;
    private final OrderValidator validator;  // Injected by Spring

    public OrderController(OrderService orderService, OrderValidator validator) {
        this.orderService = orderService;
        this.validator = validator;
    }

    @GetMapping("/orders/{id}")
    public Order getOrder(@PathVariable Long id) {
        validator.validate(id);  // Works!
        return orderService.findById(id);
    }
}

Kotlin: Spring Boot NPE patterns

// ❌ BAD — lateinit with field injection, NPE if not initialized
@Service
class NotificationService {
    @Autowired
    lateinit var emailSender: EmailSender  // NPE if accessed before injection

    fun notify(user: User) {
        emailSender.send(user.email, "Hello")  // UninitializedPropertyAccessException
    }
}

// ✅ GOOD — constructor injection, null-safe
@Service
class NotificationService(
    private val emailSender: EmailSender  // Injected, non-null
) {
    fun notify(user: User) {
        emailSender.send(user.email, "Hello")  // Always works
    }
}

Java: Spring Boot 3.x with GraalVM native image

// ❌ BAD — reflection-based injection fails in native image without hints
@Service
public class ReportService {
    @Autowired
    private ReportGenerator generator;  // NPE in native image — no reflection metadata
}

// ✅ GOOD — constructor injection + runtime hints for native image
@Service
@RegisterReflectionForBinding(ReportGenerator.class)  // Spring Boot 3.x
public class ReportService {
    private final ReportGenerator generator;

    public ReportService(ReportGenerator generator) {
        this.generator = generator;  // Works in JVM and native image
    }
}

Anti-Patterns

Wrong: Using new to create Spring beans

// ❌ BAD — Spring can't inject into objects you create with new [src3]
OrderService service = new OrderService();
service.processOrder(orderId);  // NPE — service.repo is null

Correct: Inject beans through Spring

// ✅ GOOD — let Spring wire everything [src1]
@Service
public class PaymentProcessor {
    private final OrderService orderService;  // Injected
    public PaymentProcessor(OrderService orderService) {
        this.orderService = orderService;
    }
}

Wrong: Accessing injected fields in the constructor

// ❌ BAD — @Autowired fields are null during construction [src1]
@Service
public class CacheWarmer {
    @Autowired private UserRepository userRepo;

    public CacheWarmer() {
        userRepo.findAll();  // NPE — field not yet injected!
    }
}

Correct: Use @PostConstruct for initialization logic

// ✅ GOOD — @PostConstruct runs after injection is complete [src1]
@Service
public class CacheWarmer {
    private final UserRepository userRepo;

    public CacheWarmer(UserRepository userRepo) {
        this.userRepo = userRepo;
    }

    @PostConstruct
    public void warmCache() {
        userRepo.findAll();  // Works — injection is complete
    }
}

Wrong: Using @Autowired(required=false) for required dependencies

// ❌ BAD — silently null if bean missing, NPE at runtime [src7]
@Service
public class PaymentService {
    @Autowired(required = false)
    private PaymentGateway gateway;  // null without warning

    public void charge(Order order) {
        gateway.charge(order.getTotal());  // NPE!
    }
}

Correct: Let Spring fail fast on missing required beans

// ✅ GOOD — constructor injection fails at startup if bean missing [src1]
@Service
public class PaymentService {
    private final PaymentGateway gateway;

    public PaymentService(PaymentGateway gateway) {
        this.gateway = Objects.requireNonNull(gateway);  // Belt-and-suspenders
    }

    public void charge(Order order) {
        gateway.charge(order.getTotal());  // Always works
    }
}

Common Pitfalls

Diagnostic Commands

# Enable Spring debug logging to see bean creation
# application.properties:
logging.level.org.springframework=DEBUG

# List all beans in the application context
# Add to main class:
@Bean
CommandLineRunner printBeans(ApplicationContext ctx) {
    return args -> Arrays.stream(ctx.getBeanDefinitionNames()).sorted().forEach(System.out::println);
}

# Check component scan base packages
# Look for @SpringBootApplication or @ComponentScan in your main class

# Run with debug to see auto-configuration report
java -jar app.jar --debug

# Check if a specific bean is registered (Spring Boot Actuator)
curl http://localhost:8080/actuator/beans | jq '.contexts[].beans | keys[] | select(contains("UserService"))'

# Verify AOT processing for native image (Spring Boot 3.x)
./gradlew processAot  # or: mvn spring-boot:process-aot
# Check generated sources in build/generated/aotSources

Version History & Compatibility

Spring VersionStatusKey DI ChangesMigration Notes
Spring Boot 3.4+ / Spring 6.2+ Current Revised autowiring algorithm, stricter generic matching, @Fallback beans [src8] Verify generic type signatures at injection points
Spring Boot 3.0-3.3 / Spring 6.0-6.1 Maintained Jakarta namespace (javax -> jakarta), Java 17+, AOT compilation Update all javax.inject to jakarta.inject
Spring Boot 2.7 / Spring 5.3 EOL (Nov 2023) Single-constructor injection without @Autowired (since 4.3) Upgrade to Boot 3.x for continued support
Spring Boot 2.x / Spring 5.x EOL Auto-configuration, @ConditionalOnMissingBean
Spring 4.3+ Legacy Implicit constructor injection for single-constructor beans [src1]

When to Use / When Not to Use

Use Constructor Injection WhenUse Field Injection WhenUse Setter Injection When
Always (recommended default) [src7] Legacy code only (migration in progress) Optional dependencies
Production code Never in new code Circular dependency breaking (with @Lazy)
Test-friendly code needed Quick throwaway prototypes Reconfigurable beans at runtime

Important Caveats

Related Units