Seeing APPLICATION FAILED TO START in red letters can leave you wondering where to even start. Spring Boot startup failures look scary at first, but they actually follow a fairly predictable set of patterns. This article assumes Spring Boot 3.x / Java 17+ and walks through how to go from log message to fix, classified by root cause.
Note that “startup succeeds but is slow” is a different problem. For that, see Speeding up Spring Boot startup.
Reverse lookup from error message to fix
Jump straight to the relevant section based on the error message you’re currently seeing.
| Error message you’re seeing | Main cause | Section |
|---|---|---|
Port 8080 was already in use | Port collision | Port collision |
BeanDefinitionOverrideException | Duplicate bean name registration | Duplicate / ambiguous bean definitions |
NoUniqueBeanDefinitionException | Multiple beans of the same type, can’t pick one at the injection point | Duplicate / ambiguous bean definitions |
The dependencies of some of the beans ... form a cycle | Circular reference | Circular reference |
Failed to configure a DataSource: 'url' attribute is not specified | Missing DataSource configuration | Missing DataSource configuration |
@ConditionalOnClass did not find required class | AutoConfiguration condition mismatch | AutoConfiguration not working as expected |
Binding to target ... failed | ConfigurationProperties validation failure | @ConfigurationProperties validation failure |
| No formatted message at all | Error not supported by FailureAnalyzer | How to read FailureAnalyzer messages |
If none of these match, start with “Getting the big picture of startup failures” below and read through in order.
Getting the big picture of startup failures
When Spring Boot fails to start, the first thing you see is a clean, formatted message produced by the FailureAnalyzer. Below that, the stack trace follows — a two-tier structure.
The causes you’ll meet most often boil down to roughly these seven:
- Port collision
- Duplicate / ambiguous bean definitions
- Circular references
- AutoConfiguration failures
- Missing DataSource configuration
@ConfigurationPropertiesvalidation failure- Profile configuration mistakes
The basic triage order is: formatted message → stack trace → --debug output. In most cases, the first formatted message alone is enough to identify the cause.
How to read FailureAnalyzer messages
The block emitted by FailureAnalyzer is divided into a Description field and an Action field.
***************************
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 instruction in the Action field is often the shortest path to a fix, so read it at face value first.
When the error isn’t handled by FailureAnalyzer, no formatted message appears — you only get a stack trace. In that case, the trick is to walk through the Caused by: chain from the bottom up and find the first business class or config file name that appears.
Pulling more information out with —debug
When the cause isn’t clear, turn on verbose logging. If you’re running an executable JAR, just pass --debug directly.
java -jar app.jar --debug
When launching via Maven or Gradle, how the flag is forwarded varies by version, so it’s more reliable to put it in application.properties or pass it as a JVM property.
# application.properties
debug=true
# Via JVM system property
./mvnw spring-boot:run -Dspring-boot.run.jvmArguments="-Ddebug"
./gradlew bootRun --args='--debug'
This makes the startup log emit a CONDITIONS EVALUATION REPORT. Positive matches lists the AutoConfigurations that were applied, and Negative matches lists the reasons others weren’t — making it much easier to track down why auto-configuration isn’t kicking in as expected. The mechanism is explained in detail in How Spring Boot AutoConfiguration works.
Port collision
The most common one you’ll run into is a port collision. The cause is simple: another process is already using 8080.
# macOS / Linux
lsof -i:8080
# Windows
netstat -ano | findstr 8080
Often it’s a leftover Spring Boot app from the previous run, or a running Docker container. If you can stop the offending process, you’re done.
If you really can’t kill it, just change the port in application.properties. When you want a random port for tests, specifying 0 is handy.
server.port=8081
# For tests: server.port=0 assigns a random port
Duplicate / ambiguous bean definitions
The next pair you’ll meet often are BeanDefinitionOverrideException and NoUniqueBeanDefinitionException. The names are similar and easy to confuse, but the causes are different.
BeanDefinitionOverrideException— two or more beans were registered with the same nameNoUniqueBeanDefinitionException— multiple beans of the same type exist and the injection point can’t narrow it 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;
}
}
For the former, you can suppress the error with spring.main.allow-bean-definition-overriding=true, but that’s a stopgap. The default was changed to false from Spring Boot 2.1 onward precisely to prevent unintended overrides, and enabling it brings back the risk of beans being silently replaced on a last-write-wins basis. The root cause is usually overlapping component scan ranges, so revisiting the design is the safer fix.
Circular reference
Starting with Spring Boot 2.6, circular references are forbidden by default. If A depends on B and B depends on A, you’ll get a startup error.
The dependencies of some of the beans in the application context form a cycle:
┌─────┐
| serviceA defined in file [...]
↑ ↓
| serviceB defined in file [...]
└─────┘
The log includes this loop diagram, so the dependency cycle is obvious at a glance. There are mainly three ways to fix it.
The first is switching to setter injection. Constructor injection can’t initialize when there’s a cycle, but setter injection resolves it after the bean is created.
@Service
public class ServiceA {
private ServiceB serviceB;
@Autowired
public void setServiceB(ServiceB serviceB) {
this.serviceB = serviceB;
}
}
The second is annotating one side with @Lazy so resolution is deferred through a proxy.
@Service
public class ServiceA {
private final ServiceB serviceB;
public ServiceA(@Lazy ServiceB serviceB) {
this.serviceB = serviceB;
}
}
The third is to revisit the design and separate responsibilities — extracting shared logic into a third service usually unwinds the cycle. Long term, this is the healthiest option.
If you’re really in a hurry, you can re-enable cycles with spring.main.allow-circular-references=true, but the side effect is that initialization order becomes hard to reason about — treat it as a temporary workaround. For more on beans, the Bean Lifecycle article is also worth a look.
AutoConfiguration not working as expected
When “I added the dependency but auto-configuration isn’t kicking in,” check Negative matches in the --debug output.
HibernateJpaAutoConfiguration:
Did not match:
- @ConditionalOnClass did not find required class
'org.hibernate.SessionFactory' (OnClassCondition)
In this example, the cause is likely a missing spring-boot-starter-data-jpa (or equivalent) dependency. Reasons like “this class wasn’t found” or “this bean already existed” are spelled out, making it much easier to spot missing starter dependencies or version mismatches.
Conversely, when you want to intentionally 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 fires when a DataSource class is on the classpath but no connection URL is specified, and no embedded DB driver like H2 is available either.
Failed to configure a DataSource: 'url' attribute is not specified
and no embedded datasource could be configured.
You’ll see it when you’ve added spring-jdbc or spring-boot-starter-data-jpa but forgot to add a JDBC driver, or when you use PostgreSQL in production but haven’t written the application.properties for the dev environment.
If you’re using a DB, this is the minimum that needs to be set:
spring.datasource.url=jdbc:postgresql://localhost:5432/mydb
spring.datasource.username=app
spring.datasource.password=secret
Conversely, if this app doesn’t use a DB (the dependency is only there because some library transitively pulls it in), use the exclude shown earlier to remove DataSourceAutoConfiguration. For detailed connection pool settings, see the HikariCP tuning guide.
@ConfigurationProperties validation failure
Startup also stops when a configuration value has the wrong type, or violates a constraint added with @Validated.
Binding to target ... failed:
Property: app.maxRetries
Value: "abc"
Reason: failed to convert java.lang.String to int
Since the message includes Property and Value verbatim, you just fix the corresponding key in application.properties. Common culprits are typos in key names, or a field annotated with @NotNull that didn’t actually get a value.
Startup failure caused by profile configuration mistakes
It’s easy to miss the case where the configuration values are written down but the profile doesn’t line up, so they’re never loaded.
You can specify the active profile in any of these ways:
# Environment variable
export SPRING_PROFILES_ACTIVE=dev
# JVM argument
java -jar app.jar -Dspring.profiles.active=dev
# application.properties
spring.profiles.active=dev
The naming convention is application-{profile}.properties — specifying dev loads application-dev.properties. The shared application.properties is read first, and the profile-specific file overrides it.
A classic gotcha: you put the production spring.datasource.url in application-prod.properties, forget to set SPRING_PROFILES_ACTIVE, the URL ends up null at startup, and you get Failed to configure a DataSource. Get into the habit of always checking the The following profiles are active: line at the top of the startup log.
When you still can’t figure it out
As a last resort, build a minimum reproduction environment to isolate the problem. Concretely:
- Increase logging with
logging.level.org.springframework=DEBUG - Temporarily remove beans and configuration that seem unrelated
- Pin down explicit versions for Spring Boot, Java, and the DB driver
Once it’s reproducible in that form, you’re more likely to notice the cause yourself, and you’ll also get better answers when you ask in your team chat or on Stack Overflow.
Summary
For startup failures, first read Description and Action. If that’s not enough, get more detail with --debug. Then recall the typical patterns by cause category — that flow will resolve most cases. Don’t panic; just walk through the formatted message in order — that turns out to be the shortest path.
FAQ
Q. It starts locally but fails to start only after production deploy
Most of the time it’s a missing active profile setting, or production-required environment variables (DB connection URL, API keys) not being passed to the container. After checking the The following profiles are active: line at the top of the startup log, if you’re seeing Failed to configure a DataSource, suspect that the values in application-prod.properties aren’t being read. For handling secrets, How to encrypt config files with Jasypt is also worth a look.
Q. Can I use this article for “starts but is slow” cases too?
This article is strictly for the “startup fails and stops” case. If startup just takes a long time but eventually comes UP, AutoConfiguration initialization cost and bean count dominate — see Speeding up Spring Boot startup.
Q. Is it a startup failure when some requests fail during deploy?
That’s usually not a startup failure but dropped requests caused by inadequate shutdown. See Graceful shutdown and zero-downtime deploys.
Q. I added --debug but no CONDITIONS EVALUATION REPORT shows up
When launching via Maven/Gradle, the most common cause is the flag not actually reaching the application. Either put debug=true in application.properties, or try the JVM system property approach shown earlier in this article.