Spring Boot · intermediate
Spring NoSuchBeanDefinitionException: trace the missing bean contract
Spring NoSuchBeanDefinitionException indicates the ApplicationContext could not resolve a required bean by type or qualifier during dependency injection. This guide maps the exception's nested detail message to concrete configuration boundaries—component scanning, explicit @Bean declarations, conditional annotations, and proxy generation—so engineers can trace the missing bean contract rather than guess at the cause.
The symptoms
- •Application startup fails with org.springframework.beans.factory.NoSuchBeanDefinitionException and a nested message naming either a type, a qualifier name, or a constructor parameter index that could not be satisfied.
- •ApplicationContext refresh halts before any CommandLineRunner or web server binds; logs show a BeanCreationException with the NoSuchBeanDefinitionException as the root cause rather than a child warning.
- •A single bean class fails to load while sibling beans of the same package succeed, indicating scope or annotation filtering rather than a wholesale scan failure.
- •Tests pass individually but fail under @SpringBootTest with a different active profile, suggesting profile-scoped bean visibility rather than a wiring bug.
- •Exception surfaces only when a @Lazy boundary is resolved or when an @Autowired Optional<T> is unwrapped, indicating a deferred injection path rather than eager startup wiring.
Likely causes
- •The target bean class resides outside the basePackages defined on @ComponentScan, @SpringBootApplication, or the XML equivalent, so component scanning never indexes it.
- •An annotation-based bean declaration is shadowed by an active @Conditional expression: @ConditionalOnProperty, @ConditionalOnClass, @ConditionalOnMissingBean, or @Profile evaluates to false under the current environment.
- •Two beans share the same type but no @Qualifier, @Primary, or unique bean name is supplied, so Spring cannot decide which candidate to inject and treats both as missing in some resolution paths.
- •The bean is declared in a configuration class that is itself excluded via @ComponentScan filters, @EnableAutoConfiguration excludes, or a too-narrow @SpringBootApplication(scanBasePackages=...).
- •A constructor with multiple parameters requires a registered ParameterNameDiscoverer, and the parameter name or @Qualifier does not match any registered candidate when compiled without -parameters.
- •The expected bean is produced by an auto-configuration that has been overridden or excluded via META-INF/spring.factories, AutoConfiguration.imports, or a user-supplied @Configuration with the same bean name.
First ten minutes
- 01Capture the full stack trace; locate the NoSuchBeanDefinitionException and read its message field, which names either the required type (interface/class), the qualifier, or the parameter index that failed to match.
- 02Open the source file at the line indicated by the stack trace and identify the injection site: field, constructor parameter, or method parameter that triggered resolution.
- 03Derive the expected bean contract from that injection site: required type, any @Qualifier value, expected bean name, and whether the injection is @Autowired(required=true), Optional, or @Lazy.
- 04Search the codebase for candidate producer classes: classes annotated with @Component, @Service, @Repository, @Controller, @Configuration + @Bean, and any @Conditional metadata that gates them.
- 05Compare the candidate class's fully qualified name against the @ComponentScan basePackages of the active @SpringBootApplication; a mismatch outside the scan root is the most common cause.
- 06If a candidate exists, evaluate each @Conditional expression against the active profile, property values, and classpath presence documented in application.properties, application.yml, or environment variables.
Evidence to collect
- •The exact NoSuchBeanDefinitionException message string, including any "expected at least X bean which qualifies as autowire candidate" or "no qualifying bean of type" prefix.
- •The injection site line number, target field or parameter type, and any @Qualifier or @Value annotation present at that site.
- •The fully qualified name of @SpringBootApplication and the resolved basePackages value, plus any @ComponentScan directives.
- •List of @Conditional annotations on candidate producer classes, paired with the property/class state they evaluate against at runtime.
- •Output of an ApplicationContext introspection step (such as the conditions evaluation report or actuator/beans endpoint in a non-production build) listing all beans of the expected type and their names.
Where to look
- •src/main/java tree for the component or @Configuration that should declare the missing bean, relative to the @SpringBootApplication root.
- •src/main/resources/application.properties and application.yml files for profile-scoped property keys referenced by @ConditionalOnProperty.
- •META-INF/spring.factories (legacy) and META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports for the auto-configuration class FQNs that should produce the bean.
- •ConditionalOnClass targets in the resolved classpath; a missing optional dependency silently disables an auto-configuration rather than throwing a different error.
- •Constructor parameter list of the injection target class; ensure the parameter types align with the produced bean types and that -parameters compile flag is set if parameter names are relied upon.
Diagnostic steps
- 01Reproduce the failure with the same active profile and property sources used in the failing environment; mismatched profiles are a frequent false-lead source.
- 02Confirm the candidate producer class is on the classpath using the project's compiled artifact listing or dependency tree, eliminating shaded or repackaged JAR issues.
- 03Inspect the spring-boot conditions evaluation report (debug=true) or the /actuator/beans endpoint in a non-production build to enumerate registered beans matching the expected type.
- 04Disable @Conditional evaluations one at a time (temporarily remove @Profile, @ConditionalOnProperty) to verify whether a condition is the gate; revert before commit.
- 05If multiple beans match, add @Primary to the intended candidate or supply a matching @Qualifier at the injection site to remove resolution ambiguity.
- 06If the bean is produced only by auto-configuration, verify the AutoConfiguration.imports file registers it under the current module and no user @Configuration defines a bean with the same name overriding it.
- 07If the injection site uses constructor injection without -parameters, recompile with -parameters or annotate parameters with @Qualifier/@Autowired to make resolution deterministic.
Common mistakes
- •Treating NoSuchBeanDefinitionException as a wiring typo and adding @Autowired blindly, when the underlying cause is a scan-root mismatch or a failed @Conditional gate.
- •Adding @Component on an inner class without marking it static, which Spring's component scanner does not register and yields a silent miss rather than a compile error.
- •Scattering @ComponentScan across modules without coordinating basePackages, producing one module whose beans are invisible to another's context.
- •Assuming @Profile("default") is active; an unset SPRING_PROFILES_ACTIVE and no default profile leaves no beans gated by @Profile registered.
- •Overriding an auto-configuration bean by declaring a same-named @Bean in user code, then debugging the auto-configuration as if it were missing.
- •Relying on field name autowire without @Qualifier when multiple candidates exist; Spring throws NoSuchBeanDefinitionException with "expected single matching bean but found N".
Safe fixes
- •Move the producer class into or under the @SpringBootApplication's package tree, or explicitly add its package to scanBasePackages when modular separation is required.
- •Adjust the active profile or supply the property expected by @ConditionalOnProperty so the gate evaluates true; verify with the conditions report before and after.
- •Introduce @Primary on the intended candidate, or annotate the injection site with @Qualifier("intendedBeanName") so resolution has a deterministic answer.
- •Annotate ambiguous constructor parameters with @Qualifier or recompile the module with the -parameters flag so parameter-name autowiring resolves reliably.
- •If an auto-configuration is silently disabled, add the missing optional dependency to the classpath or remove the @ConditionalOnClass gate in a local override only after documenting the deviation.
- •For nested configuration classes not picked up, annotate the enclosing @Configuration with @Import or move the @Bean method to a scanned configuration to make registration explicit.
Prove the fix
- 01Application startup completes past the BeanCreationException stage and binds the web server (or main()) with the previously failing injection site instantiated exactly once.
- 02The conditions evaluation report (debug=true) lists the formerly missing bean under the matching type with a positive evaluation outcome for every @Conditional on its producer.
- 03Where the resolution involved multiple candidates, the beans endpoint in a non-production build shows exactly one bean matching the injection site's required type and qualifier, and it is the intended candidate.
- 04Re-running the original failing test or replaying the original startup scenario no longer raises NoSuchBeanDefinitionException, and subsequent @SpringBootTest cases that exercise the same wiring pass consistently.
Prevention and next steps
- •Establish a single source of truth for the application root and document any cross-module @ComponentScan bases to prevent silent scan gaps during refactors.
- •Keep auto-configuration overrides, same-named @Bean declarations, and exclusion entries reviewed in code review so a missing bean never masks an unintended override.
- •Favor constructor injection with explicit @Qualifier annotations over field-name autowire so that resolution ambiguity becomes a compile-time concern rather than a startup failure.
- •Adopt the conditions evaluation report in CI debug builds to catch silent conditional gating before runtime, and gate the report behind a non-production flag.
- •Compile producer modules with -parameters or annotate parameters with @Qualifier so parameter-name autowiring remains deterministic across toolchain changes.
Safe commands and checks
java -jar <app-jar>.jar --debug=true > app-debug.log 2>&1; tail -n 200 app-debug.log | grep -i "Positive matches" -A 50 to enumerate beans of the expected type after resolution. mvn -q -DincludeArtifactIds=<module-artifact> dependency:tree | grep -i <expected-producer-class-fqn> to confirm the candidate class is on the resolved classpath. javap -p -c target/classes/com/example/producer/<Candidate>.class | head -n 40 to inspect compiled parameter names and verify whether -parameters retention is active. mvn -q -DskipTests compile -Dmaven.compiler.parameters=true then re-run the failing test to confirm whether parameter-name retention resolves the injection. unzip -p <app-jar>.jar BOOT-INF/classes/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports to inspect registered auto-configurations in the packaged artifact. grep -RIn "@ComponentScan\|@SpringBootApplication" src/main/java to map declared scan roots against the producer class's package and surface any boundary mismatch.