Seeing the red APPLICATION FAILED TO START banner and having no idea where to begin? Spring Boot startup failures look intimidating at first, but they actually follow a fairly limited set of patterns. This article assumes Spring Boot 3.x / Java 17+ and walks you from the log output to a fix, classifying the causes along the way.
Note that an application that starts but does so slowly is a different problem. For that, see Speeding up Spring Boot startup. Likewise, if only some requests fail every time you deploy, that is not a startup failure either. The likely culprit is requests being dropped during shutdown, so check Graceful shutdown and zero-downtime deployment.
Look Up the Fix from the Error Message
Jump straight from the error message you are looking at right now to the matching section.
| Error message you see | Main cause | Section |
|---|---|---|
Port 8080 was already in use | Port conflict | Port Conflicts |
BeanDefinitionOverrideException | Two Beans registered with the same name | Duplicate and Ambiguous Bean Definitions |
NoUniqueBeanDefinitionException | Multiple Beans of the same type; injection point cannot pick one | Duplicate and Ambiguous Bean Definitions |
The dependencies of some of the beans ... form a cycle | Circular reference | Circular References |
Failed to configure a DataSource: 'url' attribute is not specified | Missing DataSource configuration | Missing DataSource Configuration |
@ConditionalOnClass did not find required class | AutoConfiguration condition not met | AutoConfiguration Not Behaving as Expected |
Binding to target ... failed | ConfigurationProperties validation failure | @ConfigurationProperties Validation Failures |
| No formatted message at all | Error not covered by a FailureAnalyzer | How to Read FailureAnalyzer Messages |
If none of these match, start with “Getting the Big Picture of Startup Failures” below and read on from there.
Getting the Big Picture of Startup Failures
When Spring Boot fails to start, the first thing you get is a human-readable message formatted by a FailureAnalyzer. The stack trace follows below it, giving you a two-layer structure.
The causes you run into most often boil down to roughly these seven:
- Port conflicts
- Duplicate or ambiguous Bean definitions
- Circular references
- AutoConfiguration failures
- Missing DataSource configuration
@ConfigurationPropertiesvalidation failures- Profile misconfiguration
The basic troubleshooting order is: formatted message → stack trace → --debug output. In most cases the formatted message alone tells you the cause.
How to Read FailureAnalyzer Messages
The block printed by a FailureAnalyzer is split into a Description section and an Action section.
***************************
APPLICATION FAILED TO START
***************************
Description:
Web server failed to start. Port 8080 was already in use.
Action:
Identify and stop the process that's listening on port 8080 or configure this application to listen on another port.
Description tells you what happened, and Action tells you what to do next. The instructions under Action are very often the shortest path to a fix, so start by taking them at face value.
If the error is not covered by any FailureAnalyzer, you get no formatted message, just a stack trace. In that case, the trick is to follow the Caused by: lines from the bottom up and find the first one that mentions one of your own classes or a configuration file name.
Getting More Information with the —debug Flag
When the cause is not obvious, turn on verbose logging. With an executable JAR you can pass --debug directly.
java -jar app.jar --debug
When launching through Maven or Gradle, how the flag gets passed through varies by version, so it is more reliable to set it in application.properties or as a JVM property.
# application.properties
debug=true
# JVMシステムプロパティで指定する場合
./mvnw spring-boot:run -Dspring-boot.run.jvmArguments="-Ddebug"
./gradlew bootRun --args='--debug'
The startup log will then include a CONDITIONS EVALUATION REPORT. Positive matches lists the AutoConfigurations that were applied, and Negative matches lists the ones that were not along with the reason, which makes it much easier to find out why auto-configuration is not kicking in as expected. The underlying mechanism is covered in detail in How Spring Boot AutoConfiguration works.
Port Conflicts
The one you will hit most often is a port conflict. The cause is simple: another process is already using port 8080.
# macOS / Linux
lsof -i:8080
# Windows
netstat -ano | findstr 8080
Common culprits are a previous Spring Boot instance still running or a Docker container that is still up. Stop the offending process and you are done.
If you really cannot stop it, change the port in application.properties. For tests, specifying 0 is a convenient way to get a random port.
server.port=8081
# テスト時は server.port=0 でランダム割当
Duplicate and Ambiguous Bean Definitions
The next most common pair is BeanDefinitionOverrideException and NoUniqueBeanDefinitionException. The names look similar and are easy to mix up, but the causes differ.
BeanDefinitionOverrideExceptionoccurs when two or more Beans are registered under the same nameNoUniqueBeanDefinitionExceptionoccurs when multiple Beans of the same type exist and the injection point cannot narrow them down to one
The latter can be resolved with @Primary or @Qualifier.
@Service
public class OrderService {
private final PaymentGateway gateway;
public OrderService(@Qualifier("stripeGateway") PaymentGateway gateway) {
this.gateway = gateway;
}
}
The former can be suppressed with spring.main.allow-bean-definition-overriding=true, but treat that as a stopgap. The default was changed to false in Spring Boot 2.1 precisely to prevent unintended overrides, and enabling it means the last definition silently wins and replaces the Bean. The root cause is usually overlapping component scan ranges, so revisiting the design is the safer fix.
Circular References
Since Spring Boot 2.6, circular references are prohibited by default. If A depends on B and B depends on A, startup fails.
The dependencies of some of the beans in the application context form a cycle:
┌─────┐
| serviceA defined in file [...]
↑ ↓
| serviceB defined in file [...]
└─────┘
The log prints this dependency diagram, so the cycle is visible at a glance. There are three main ways to deal with it.
The first is switching to setter injection. Constructor injection cannot initialize when there is a cycle, but setter injection resolves the dependency after the Beans have been created.
@Service
public class ServiceA {
private ServiceB serviceB;
@Autowired
public void setServiceB(ServiceB serviceB) {
this.serviceB = serviceB;
}
}
The second is adding @Lazy to one side so the dependency is resolved lazily through a proxy.
@Service
public class ServiceA {
private final ServiceB serviceB;
public ServiceA(@Lazy ServiceB serviceB) {
this.serviceB = serviceB;
}
}
The third is revisiting the design and separating responsibilities. Extracting the shared logic into a third service breaks the cycle. In the long run this is the healthiest option.
If you are truly pressed for time, you can re-enable circular references with spring.main.allow-circular-references=true, but it has the side effect of making the initialization order harder to reason about, so treat it strictly as a temporary measure. For more on Bean-related topics, the Bean Lifecycle article is also worth a look.
AutoConfiguration Not Behaving as Expected
When you have added a dependency but the auto-configuration is not kicking in, look at Negative matches in the --debug output.
HibernateJpaAutoConfiguration:
Did not match:
- @ConditionalOnClass did not find required class
'org.hibernate.SessionFactory' (OnClassCondition)
In this example, you can infer that a dependency equivalent to spring-boot-starter-data-jpa is missing. The report spells out why each condition was not met, such as “this class was not found” or “this Bean already existed”, which makes missing starter dependencies and version mismatches easy to spot.
Conversely, if you want to deliberately disable a specific AutoConfiguration, use exclude.
@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Missing DataSource Configuration
This one appears when the DataSource class is on the classpath but no connection URL is specified, and no embedded database driver such as H2 can be found either.
Failed to configure a DataSource: 'url' attribute is not specified
and no embedded datasource could be configured.
You run into it when you have added spring-jdbc or spring-boot-starter-data-jpa but forgot to add the JDBC driver, or when production uses PostgreSQL but you never wrote an application.properties for the dev environment.
If the application does use a database, this is the minimum you need to set.
spring.datasource.url=jdbc:postgresql://localhost:5432/mydb
spring.datasource.username=app
spring.datasource.password=secret
If, on the other hand, the application does not use a database at all (the dependency is only there because of a library), remove DataSourceAutoConfiguration with the exclude shown earlier. For detailed connection pool settings, see the HikariCP tuning guide.
@ConfigurationProperties Validation Failures
Startup also halts when a configuration value has the wrong type or violates a constraint declared with @Validated.
Binding to target ... failed:
Property: app.maxRetries
Value: "abc"
Reason: failed to convert java.lang.String to int
The message shows the Property and Value directly, so fixing the corresponding key in application.properties resolves it. Typos in key names and fields annotated with @NotNull that have no value are the classic patterns.
Startup Failures Caused by Profile Misconfiguration
Another easy one to miss: the configuration value is written down, but the profile does not line up, so it never gets loaded.
The active profile is specified in one of the following ways.
# 環境変数
export SPRING_PROFILES_ACTIVE=dev
# JVM引数
java -jar app.jar -Dspring.profiles.active=dev
# application.properties
spring.profiles.active=dev
The naming convention is application-{profile}.properties, so specifying dev loads application-dev.properties. The shared application.properties is read first, and the profile-specific file overrides it.
A typical scenario: you put the production spring.datasource.url in application-prod.properties, forget to set SPRING_PROFILES_ACTIVE, and the URL ends up null at startup, producing Failed to configure a DataSource. Make it a habit to always check the The following profiles are active: line near the top of the startup log.
When the application starts locally but fails only after deploying to production, the cause is almost always either this missing profile setting or environment variables such as the DB connection URL or API keys not being passed into the container. For handling sensitive values, see also How to encrypt configuration files with Jasypt.
When You Still Cannot Figure It Out
As a last resort, build a minimal reproduction to isolate the problem. Concretely, do these three things:
- Increase logging with
logging.level.org.springframework=DEBUG - Temporarily remove Beans and configuration that seem unrelated
- Pin down the exact Spring Boot / Java / DB driver versions
Reproducing the issue in this form makes it easier to notice the cause yourself, and it also makes it much easier to get answers when you ask in a team chat or on Stack Overflow.
Summary
For startup failures, read the Description and Action first. If that is not enough, get more detail with --debug. Then recall the typical patterns for each category of cause. That flow resolves most cases. Staying calm and working through the formatted message step by step is, in the end, the fastest route.