Spring Boot Won't Start! Diagnose 6 Causes of UnsatisfiedDependencyException in 5 Minutes
The app that started fine yesterday died this morning under 200 lines of red stack trace. Error creating bean with name ..., No qualifying bean of type ... available. A screen full of nested exception makes your stomach drop, but here's the short version: don't panic—start reading from the last line.
This post is not about runtime errors in an already-running app—JWT auth failures, Whitelabel 404s, and the like. It covers only bean registration and injection failures that prevent the application from starting at all. There are essentially only six failure patterns at this stage, and once you pinpoint the cause, most of them are a one-line fix.
How to Read a Stack Trace Backwards (Like ssh -vvv)
Read a stack trace from the top and you'll get lost. Like debugging ssh -vvv logs, you have to read from the bottom up to see the real cause. Here's a real example.
Error creating bean with name 'orderController': ← ① 누가: orderController 생성 중
Unsatisfied dependency expressed through constructor parameter 0; ← ② 무엇을: 생성자 0번 파라미터 주입 실패
nested exception is
org.springframework.beans.factory.NoSuchBeanDefinitionException:
No qualifying bean of type 'com.example.PaymentService' available: ← ③ 왜: PaymentService 빈이 없음
expected at least 1 bean which qualifies as autowire candidatePull out just these three elements and you're done.
- Who (the bean that failed to create):
orderController - What (what it tried to inject): constructor parameter 0
- Why (root cause):
NoSuchBeanDefinitionException→ noPaymentServicebean in the container
The exception type on the last line tells you which way to look.
| Last-line exception message | Meaning | Likely cause |
|---|---|---|
NoSuchBeanDefinition: No qualifying bean ... expected at least 1 | 0 beans | Missing component / outside scan range / conditional bean not created |
NoUniqueBeanDefinitionException: expected single matching bean but found 2 | 2+ beans | Multiple registrations of the same type; need @Qualifier |
Requested bean is currently in creation / circular reference | Mutual references | Constructor circular reference |
Diagnosis Table for 6 Causes + Copy-Paste Fixes
Here's the mapping table at a glance.
| # | Error clue | Cause | Fix |
|---|---|---|---|
| 1 | No qualifying bean ... expected at least 1 | Missing @Component or outside scan range | Add the annotation / @ComponentScan(basePackages=...) |
| 2 | expected single matching bean but found 2 | Multiple implementations | @Qualifier / @Primary |
| 3 | currently in creation | Constructor circular reference | Split responsibilities / @Lazy |
| 4 | Bean missing entirely | @ConditionalOnProperty / @Profile not met | Enable the property/profile |
| 5 | NoUniqueBeanDefinitionException | Two beans of the same type | @Primary or explicit injection |
| 6 | Fails only in tests | Bean missing from the slice context | @MockBean / adjust slice scope |
Cause 1. Missing component or outside scan range
This is the most common one. @SpringBootApplication only scans its own package and subpackages. If a bean lives under a separate root like com.example.common, it won't be picked up.
@SpringBootApplication
@ComponentScan(basePackages = {"com.example.app", "com.example.common"})
public class Application { }And plenty of people just forget @Service/@Component on the class itself—check that first.
Causes 2 & 5. Multiple implementations → @Qualifier / @Primary
If PaymentService has two implementations, KakaoPayService and NaverPayService, Spring has no idea which one to inject and throws NoUniqueBeanDefinitionException.
@Service @Qualifier("kakaoPay")
public class KakaoPayService implements PaymentService { }
@Service @Primary // 기본 후보 지정
public class NaverPayService implements PaymentService { }
// 주입 시 명시 선택
public OrderController(@Qualifier("kakaoPay") PaymentService paymentService) { ... }Cause 3. Constructor circular reference → @Lazy
If A injects B via constructor and B injects A, that's a circular reference. From Spring Boot 2.6+ the default of spring.main.allow-circular-references is false, so startup fails immediately (problems that used to hide behind field injection now surface early thanks to constructor injection being the recommended style). The right fix is to split responsibilities, but when you need it now:
public AService(@Lazy BService bService) {
this.bService = bService;
}Cause 4. Conditional bean never created
If the @ConditionalOnProperty condition isn't met, the bean is never created at all.
@Bean
@ConditionalOnProperty(name = "feature.pay.enabled", havingValue = "true")
public PaymentService paymentService() { ... }→ You need feature.pay.enabled: true in application.yml for the bean to appear. Same story with @Profile("prod").
Cause 6. Fails only in tests
@WebMvcTest loads only the controller slice, so service beans aren't there. Fill in the dependencies with @MockBean.
@WebMvcTest(OrderController.class)
class OrderControllerTest {
@MockBean PaymentService paymentService;
@Autowired MockMvc mockMvc;
}A note from the field
The first thing I do after scaffolding a new project is collapse the package structure under a single com.example.app root. Cause 1—forgetting basePackages with multi-root packages—accounts for more than half of all bean-injection issues I see. And when an auto-config bean doesn't appear, it's faster to suspect a missing dependency in build.gradle than to start reading library source.
Common Pitfalls & Verification Routine
- Multi-module: If beans from the
coremodule aren't picked up in theapimodule, check that the dependency is there and that the package is included in the scan path (@ComponentScan). - Auto-config bean missing because of a missing dependency: Without the library,
@ConditionalOnClassauto-configuration is disabled entirely.
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'- The trap of
@Autowired(required=false)/Optionalworkarounds: The error disappears, but you get an NPE later at runtime. It's only a workaround that hides the root cause. - Check actually registered beans with Actuator: Look instead of guessing.
management.endpoints.web.exposure.include=beansHit /actuator/beans and check whether the bean name is in the list—you'll immediately know whether it wasn't scanned or wasn't created because of a condition. Note that Spring Boot 3.x switched to the Jakarta namespace (jakarta.*), so javax.*-based libraries can be dropped from bean scanning/auto-config. With GraalVM Native Image, reflection-based bean registration can be missing at build time and needs extra hints.
Conclusion: One-Page Diagnosis Checklist
- Read the stack trace from the bottom and check the exception type
No qualifying bean→ missing component / scan range / conditional beanexpected single ... found 2→@Qualifier/@Primarycurrently in creation→ circular reference; split design or@Lazy- Fails only in tests → slice scope +
@MockBean - When in doubt, check
/actuator/beansfor actual registration
Those "200 red lines" are really just bodyguards for the last line. Make reading backwards a habit, and get into the routine of checking the bean list with Actuator ahead of time—startup failures stop being scary.
FAQ
Q. I added @Component but still get No qualifying bean.
A. It's outside the scan range almost 100% of the time. If that class's package is not a subpackage of where @SpringBootApplication lives, spell it out in @ComponentScan(basePackages=...).
Q. @Repository is picked up but my class isn't. A. That's a signal that the Repository is inside auto-config/scan range and only your class is outside. Check both the package location and whether the annotation is actually on the class.
Q. Bean injection fails only in tests.
A. Slices like @WebMvcTest and @DataJpaTest load only a subset of beans. Fill required dependencies with @MockBean, or use @SpringBootTest if you need the full context.
Q. How do I know it's a circular reference?
A. If the message contains currently in creation or circular reference, that's definitive. Spring Boot 2.6+ blocks it by default, so it shows up immediately at startup.
Q. With two implementations, can I just always use @Primary?
A. @Primary is appropriate only when there is clearly one default implementation. If you need to choose per use site, it's safer and more explicit to use @Qualifier at each injection point.
Nodelog는 모든 콘텐츠의 내용과 출처를 공개 전에 검토합니다. 환경(OS·버전)에 따라 결과가 달라질 수 있는 기술 정보는 공식 문서와 함께 확인하며, 검토 기준과 정정 원칙은 편집 정책에서 안내합니다. 오류를 발견하시면 이메일로 제보해 주세요 — 확인 후 신속히 정정합니다.
Comments
Be the first to comment.