---
# === IDENTITY ===
id: software/debugging/spring-boot-npe/2026
canonical_question: "How do I fix NullPointerException in Spring Boot?"
aliases:
  - "Spring Boot NullPointerException"
  - "NPE in Spring Boot"
  - "Spring autowired null"
  - "Spring bean injection null"
  - "NullPointerException Spring service"
  - "@Autowired field is null"
  - "Spring Boot null injection"
  - "Spring dependency injection null"
entity_type: software_reference
domain: software > debugging > spring_boot_npe
region: global
jurisdiction: global
temporal_scope: 2018-2026

# === VERIFICATION ===
last_verified: 2026-05-17
confidence: 0.94
version: 2.1
first_published: 2026-02-17

# === TEMPORAL VALIDITY ===
temporal_validity:
  status: stable
  last_breaking_change: "Spring Boot 4.0 / Spring Framework 7.0 (2025-11) — JSpecify null-safety annotations adopted across the portfolio; javax.inject and javax.annotation packages no longer supported"
  next_review: 2026-11-13
  change_sensitivity: medium

# === CONSTRAINTS ===
constraints:
  - "Spring only injects into beans it manages — objects created with `new` never receive injection"
  - "Spring Boot 3.0+ requires Java 17 minimum and jakarta.* namespace — javax.inject will not work; Spring Boot 4.0+ drops javax.annotation entirely"
  - "Spring Boot 4.0 / Spring Framework 7.0 (Nov 2025) adopts JSpecify annotations (@Nullable, @NullMarked) across the portfolio — IDEs and NullAway flag unchecked null usages at compile time"
  - "AOT/native-image builds require runtime hints for reflection-based injection — missing hints cause NPE at runtime"
  - "Spring Framework 6.2+ uses a revised autowiring algorithm — generic type matching is stricter and may break injections that worked in 6.1"
  - "@Autowired(required=false) silently sets fields to null — never use on required dependencies"

# === SKIP CONDITIONS ===
skip_this_unit_if:
  - condition: "The NPE is in a Spring WebFlux reactive pipeline (Mono/Flux .map/.flatMap chain), not classic MVC injection"
    use_instead: "Reactive-specific NPE: handle empty Mono with .switchIfEmpty(); use defaultIfEmpty for Flux"
  - condition: "The error is BeanCreationException, UnsatisfiedDependencyException, or NoSuchBeanDefinitionException — the bean failed to start, it isn't null at runtime"
    use_instead: "Read the startup BeanCreationException stack trace; this unit only covers runtime NPE after the context is up"
  - condition: "The application uses Spring Boot 4.0+ and the compiler/IDE is flagging a JSpecify @Nullable/@NullMarked warning — that is a static-analysis issue, not a runtime NPE"
    use_instead: "Add a null check or annotate the parameter @Nullable; this unit covers runtime NPE root causes, not JSpecify static-analysis findings"

# === AGENT HINTS ===
inputs_needed:
  - key: spring_version
    question: "Which Spring Boot version are you using?"
    type: choice
    options: ["Spring Boot 2.x", "Spring Boot 3.x (3.0-3.3)", "Spring Boot 3.4+", "Spring Boot 4.0+ (Nov 2025)"]
  - key: npe_location
    question: "Where does the NullPointerException occur?"
    type: choice
    options: ["@Autowired field is null", "In constructor/init", "In test class", "In filter/interceptor", "In @Scheduled method", "Other"]
  - key: build_type
    question: "How do you build and run the application?"
    type: choice
    options: ["Standard JVM (jar/war)", "GraalVM native image", "Docker container"]

# === DISTRIBUTION ===
canonical_source: "https://knowledgelib.io/software/debugging/spring-boot-npe/2026"
suggested_citation: "Source: knowledgelib.io — AI Knowledge Library (verified 2026-05-17)"

# === RELATED UNITS ===
related_kos:
  related_to:
    - id: "software/debugging/spring-boot-503/2026"
      label: "Spring Boot 503 Service Unavailable"
    - id: "software/debugging/java-classnotfoundexception/2026"
      label: "Java ClassNotFoundException Debugging"
    - id: "software/debugging/java-outofmemoryerror/2026"
      label: "Java OutOfMemoryError Debugging"
  often_confused_with:
    - id: "software/debugging/js-cannot-read-property-undefined/2026"
      label: "JavaScript: Cannot read property of undefined (different ecosystem, same null-reference family)"

# === SOURCES (5-8 authoritative sources) ===
sources:
  - id: src1
    title: "Spring Framework Reference — Dependency Injection"
    author: Spring.io
    url: https://docs.spring.io/spring-framework/reference/core/beans/dependencies.html
    type: official_docs
    published: 2025-09-01
    reliability: authoritative
  - id: src2
    title: "Common Spring Boot Mistakes"
    author: Baeldung
    url: https://www.baeldung.com/spring-boot-common-mistakes
    type: technical_blog
    published: 2025-06-15
    reliability: high
  - id: src3
    title: "Why is my Spring @Autowired field null?"
    author: Stack Overflow
    url: https://stackoverflow.com/questions/19896870/why-is-my-spring-autowired-field-null
    type: community_resource
    published: 2025-01-01
    reliability: high
  - id: src4
    title: "Spring Boot Testing Documentation"
    author: Spring.io
    url: https://docs.spring.io/spring-boot/reference/testing/
    type: official_docs
    published: 2025-09-01
    reliability: authoritative
  - id: src5
    title: "Spring Data JPA Reference"
    author: Spring.io
    url: https://docs.spring.io/spring-data/jpa/reference/
    type: official_docs
    published: 2025-08-01
    reliability: authoritative
  - id: src6
    title: "Spring Security Context and Authentication"
    author: Spring.io
    url: https://docs.spring.io/spring-security/reference/
    type: official_docs
    published: 2025-09-01
    reliability: authoritative
  - id: src7
    title: "Spring @Autowired Field Null — Common Causes and Solutions"
    author: Baeldung
    url: https://www.baeldung.com/spring-autowired-field-null
    type: technical_blog
    published: 2025-03-10
    reliability: high
  - id: src8
    title: "Spring Framework 6.2 Release Notes"
    author: Spring.io
    url: https://github.com/spring-projects/spring-framework/wiki/Spring-Framework-6.2-Release-Notes
    type: official_docs
    published: 2025-11-01
    reliability: authoritative
  - id: src9
    title: "Null-safe applications with Spring Boot 4"
    author: Spring.io
    url: https://spring.io/blog/2025/11/12/null-safe-applications-with-spring-boot-4/
    type: official_docs
    published: 2025-11-12
    reliability: authoritative
---

# How to Fix NullPointerException in Spring Boot

## How do I fix NullPointerException in Spring Boot?

## TL;DR

- **Bottom line**: 90% of Spring Boot NPEs come from 4 root causes: (1) calling `new` instead of letting Spring inject the bean, (2) missing `@Component`/`@Service` annotation, (3) calling injected fields before construction completes, or (4) accessing a scoped bean outside its scope.
- **Key tool/command**: `@Autowired` debug — check the stack trace for `null` field access, then verify the class is a Spring-managed bean (not `new`-instantiated).
- **Watch out for**: `@Autowired` on fields in classes created with `new MyClass()` — Spring only injects into beans it manages.
- **2026 update**: Spring Boot 4.0 / Spring Framework 7.0 (Nov 2025) adopt JSpecify `@Nullable`/`@NullMarked` annotations across the portfolio — IntelliJ 2025.3+ and NullAway catch potential NPEs at compile time. Migrate to JSpecify annotations on your own APIs to get the same protection. [src9]
- **Works with**: Spring Boot 2.x-4.x, Spring Framework 5.x-7.x, Java 17+ (Boot 3.x), Java 17+ baseline on Boot 4.x (Java 25 recommended for full JSpecify/NullAway).

## Constraints
<!-- Agents: read this section before recommending any action from this unit.
     These are hard constraints that must not be violated. -->

- Spring only injects into beans it manages — objects created with `new` never receive injection. [src1]
- Spring Boot 3.0+ requires Java 17 minimum and `jakarta.*` namespace — `javax.inject` annotations will not work. [src8]
- AOT/native-image builds (GraalVM) require runtime hints for reflection-based injection — missing hints cause NPE at runtime even when the bean exists. [src1]
- Spring Framework 6.2+ uses a revised autowiring algorithm with stricter generic type matching — injections that worked in 6.1 may fail silently in 6.2. [src8]
- `@Autowired(required=false)` silently sets fields to `null` without any startup error — never use on required dependencies. [src7]

## Quick Reference

| # | NPE Pattern | Likelihood | Signature | Fix |
|---|---|---|---|---|
| 1 | `@Autowired` field is null | ~40% of cases | `this.someService is null` in stack trace | Class instantiated with `new` — let Spring manage the bean [src3] |
| 2 | `@Autowired` field null (bean exists) | ~20% of cases | Bean class has no stereotype annotation | Add `@Component`/`@Service`/`@Repository` [src1] |
| 3 | NPE in constructor | ~10% of cases | NPE in `<init>` method | Move logic to `@PostConstruct` or use constructor injection [src1] |
| 4 | NPE in `@Scheduled` method | ~5% of cases | Method is `static` or class not a bean | Ensure class is `@Component` and method is non-static [src2] |
| 5 | NPE accessing `Optional.get()` | ~8% of cases | `NoSuchElementException` (similar) | Use `Optional.orElseThrow()` or `Optional.orElse(null)` [src5] |
| 6 | NPE in filter/interceptor | ~5% of cases | Filter registered outside Spring context | Register filter as `@Bean` via `FilterRegistrationBean` [src6] |
| 7 | NPE in test | ~8% of cases | Using `@Mock` without `@InjectMocks` or `@MockBean` | Use `@SpringBootTest` + `@MockBean` for integration tests [src4] |
| 8 | NPE with `@Value` | ~4% of cases | Property not set or class not a bean | Check `application.properties` and ensure class is Spring-managed [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 → Enable debug logging: 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

```java
// ❌ 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

```java
// ❌ 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)

```java
// ❌ 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

```java
// 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: Complete example — common NPE scenario and fix

> Full script: [java-complete-example-common-npe-scenario-and-fix.java](scripts/java-complete-example-common-npe-scenario-and-fix.java) (57 lines)

```java
// 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
# ... (see full script)
```

### Kotlin: Spring Boot NPE patterns

```kotlin
// ❌ 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

```java
// ❌ 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

```java
// ❌ 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

```java
// ✅ 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

```java
// ❌ 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

```java
// ✅ 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

```java
// ❌ 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

```java
// ✅ 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

- **`new` keyword bypasses Spring DI**: The #1 cause of `@Autowired` NPEs. If you call `new MyService()`, Spring has no chance to inject dependencies. Fix: always inject beans through constructors or `@Autowired`. [src3]
- **Missing `@Component` scan**: If the class is in a package not scanned by `@SpringBootApplication`, Spring won't discover it. Fix: ensure the class is in the same package or a sub-package of the main application class. [src2]
- **Static methods accessing instance fields**: A `static` method can't access `@Autowired` instance fields. Fix: make the method non-static, or inject the bean into a static holder. [src2]
- **Circular dependencies causing partial initialization**: Two beans depending on each other can cause NPEs if one isn't fully initialized. Fix: use `@Lazy` on one dependency, or restructure to break the cycle. [src1]
- **Test context not loaded**: Using `@Autowired` in tests without `@SpringBootTest` results in null fields. Fix: use `@SpringBootTest` for integration tests or `@ExtendWith(MockitoExtension.class)` + `@InjectMocks` for unit tests. [src4]
- **`Optional.get()` without checking**: JPA `findById()` returns `Optional`. Calling `.get()` on empty optional throws `NoSuchElementException` (not NPE, but similar). Fix: use `.orElseThrow()`. [src5]
- **Spring Framework 6.2 stricter generic matching**: After upgrading to Spring 6.2+, generic type matching is stricter. A bean that matched by type in 6.1 may not match in 6.2, causing null injection. Fix: verify generic signatures at injection points. [src8]
- **GraalVM native image missing reflection metadata**: Code works on JVM but NPEs in native image because AOT compilation can't discover reflection-based beans. Fix: add `@RegisterReflectionForBinding` or provide `RuntimeHints`. [src1]

## Diagnostic Commands

```bash
# 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 Version | Status | Key DI Changes | Migration Notes |
|---|---|---|---|
| Spring Boot 4.0 / Spring Framework 7.0 (Nov 2025) | Current | JSpecify `@Nullable`/`@NullMarked` adopted across the portfolio; `javax.annotation` and `javax.inject` annotations no longer supported; SpringExtension uses test-method scoped ExtensionContext [src9] | Migrate any remaining `javax.inject`/`javax.annotation` to `jakarta.*`; add JSpecify deps and `@NullMarked` packages to your own modules; verify custom `TestExecutionListener` for the new ExtensionContext scope |
| Spring Boot 3.5 (LTS through Nov 2026) | Maintained | Compatible with Spring Framework 6.2 autowiring algorithm | Final 3.x line; plan upgrade to Boot 4 |
| Spring Boot 3.4 / Spring 6.2 | Maintained | 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 or 4.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 When | Use Field Injection When | Use 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

- Constructor injection is the Spring team's recommended approach since Spring 4.3. It makes NPEs impossible for required dependencies because the bean can't be created without all dependencies. [src1]
- `@Autowired(required = false)` can introduce NPEs silently — the field will be null if no bean is found, without any error at startup. [src7]
- Spring Boot 3.x with native compilation (GraalVM) may have different DI behavior — ensure all beans are discoverable at build time. Add `@RegisterReflectionForBinding` where needed.
- Spring Framework 6.2 introduced a revised autowiring algorithm where `@Qualifier` matches now overrule `@Priority` ranking (reversed from 6.1). This can change which bean gets injected. [src8]
- Spring Boot 3.4+ introduces `@Fallback` beans — a fallback bean is used only when no other bean of that type is defined, which can mask injection issues if not understood. [src8]
- Spring Boot 4.0 / Spring Framework 7.0 (Nov 2025) drops `javax.annotation` and `javax.inject` entirely — any code still relying on `@javax.annotation.PostConstruct` or `@javax.inject.Inject` will fail to compile or to inject after upgrade. Migrate to `jakarta.*` equivalents. [src9]
- Spring Boot 4.0 portfolio APIs are JSpecify-annotated. If you wire your own beans through generic Spring APIs and your module is `@NullMarked`, parameters that used to silently accept null may now require explicit `@Nullable` — otherwise NullAway/IDE errors flag them at compile time before they reach production. [src9]

## Related Units

- [Spring Boot 503 Service Unavailable](/software/debugging/spring-boot-503/2026)
- [Java ClassNotFoundException Debugging](/software/debugging/java-classnotfoundexception/2026)
- [Java OutOfMemoryError Debugging](/software/debugging/java-outofmemoryerror/2026)
