skip to content

FailureAnalyzer diagnostics

FailureAnalyzers turn raw startup exceptions into readable diagnoses like 'port 8080 is already in use', and you can register your own. Interviewers use it to test whether you know that friendly startup error is a pluggable feature.

part ofSpring Frameworkoverview, primer and where to startread it →
on this pageshow

questions

5

How do you write and register a custom FailureAnalyzer for your own startup exception?

level: seniorimportance: must knowfreq 30%

answer

  1. extends AbstractFailureAnalyzer<MyException>
  2. return new FailureAnalysis(description, action, cause)
  3. register in META-INF/spring.factories
  4. key = org.springframework.boot.diagnostics.FailureAnalyzer
  5. NOT a bean, NOT the .imports file

basics

~10 s

Extend AbstractFailureAnalyzer<YourException>, override analyze() to return a FailureAnalysis with a description and action, then register the class name in META-INF/spring.factories under the org.springframework.boot.diagnostics.FailureAnalyzer key.

solid answer

~40 s

Create a class extending `AbstractFailureAnalyzer<T>` where `T` is your exception type, and override `protected FailureAnalysis analyze(Throwable rootFailure, T cause)` to return a `new FailureAnalysis(description, action, cause)`. The description says what broke; the action gives concrete fix steps. Then register it — crucially, **not** as a `@Bean` (the context is broken when it runs) — but by listing the fully-qualified class name in `src/main/resources/META-INF/spring.factories` under the key `org.springframework.boot.diagnostics.FailureAnalyzer`. Spring Boot loads it via `SpringFactoriesLoader` at startup, so it's available even though no application context exists yet. If your analyzer needs the `Environment` or `BeanFactory` (e.g. to read a property in the message), implement `EnvironmentAware` / `BeanFactoryAware` and Boot will inject them before analysis.

go deeper

for a junior

Know it involves extending a base class and returning description+action.

for a middle

Write the AbstractFailureAnalyzer subclass and know the spring.factories key.

for a senior

Nail the spring.factories-not-imports-not-bean registration and EnvironmentAware/BeanFactoryAware injection.

for a principal

Design a diagnostics story for a library/starter: which exceptions to analyze, message quality, and packaging analyzers in the starter's spring.factories.

## Step 1 — the analyzer class Extend `AbstractFailureAnalyzer<T>` typed to the exception you throw during startup: ```java package com.example.diagnostics; import org.springframework.boot.diagnostics.AbstractFailureAnalyzer; import org.springframework.boot.diagnostics.FailureAnalysis; public class MissingApiKeyFailureAnalyzer extends AbstractFailureAnalyzer<MissingApiKeyException> { @Override protected FailureAnalysis analyze(Throwable rootFailure, MissingApiKeyException cause) { String description = "No API key was configured for the payment client."; String action = "Set 'app.payment.api-key' in application.yml, " + "or provide the APP_PAYMENT_API_KEY environment variable."; return new FailureAnalysis(description, action, cause); } } ``` Throw `MissingApiKeyException` from wherever your bean initialization validates config; the wrapper exceptions around it don't matter because `AbstractFailureAnalyzer` unwraps the chain to find your type. ## Step 2 — register it (the part people get wrong) FailureAnalyzers are discovered via `SpringFactoriesLoader`, so you list them in **`src/main/resources/META-INF/spring.factories`**: ```properties org.springframework.boot.diagnostics.FailureAnalyzer=\ com.example.diagnostics.MissingApiKeyFailureAnalyzer ``` Multiple analyzers are comma-separated (with `\` line continuations). **Do not** declare the analyzer as a `@Bean` or `@Component` — it would never be picked up for its purpose, because when analysis runs the application context has already failed to start, so there are no beans to consult. ### Why not the `.imports` file? Spring Boot 2.7/3.x moved **auto-configuration** registration out of `spring.factories` into `META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports`. That change applies **only to auto-configuration classes**. `FailureAnalyzer` registration still lives in `spring.factories` under its own key. Putting an analyzer in an `.imports` file will silently do nothing. ## Step 3 — (optional) access Environment / BeanFactory Because there's no context, you can't `@Autowired` anything. Instead implement Boot's aware interfaces and the diagnostics infrastructure injects them before calling `analyze`: ```java public class MissingApiKeyFailureAnalyzer extends AbstractFailureAnalyzer<MissingApiKeyException> implements org.springframework.context.EnvironmentAware { private org.springframework.core.env.Environment environment; @Override public void setEnvironment(org.springframework.core.env.Environment e) { this.environment = e; } // use this.environment inside analyze(...) to build a smarter action } ``` `BeanFactoryAware` works the same way. Only `Environment` and `BeanFactory` injection are supported. ## Testing You can unit-test the analyzer directly by constructing your exception and asserting on the returned `FailureAnalysis.getDescription()` / `getAction()` — no Spring context needed. ## Gotchas recap - Register in `spring.factories`, NOT `.imports`, NOT as a bean. - Type the generic to your real exception, not to a wrapper. - Return `null` from `analyze` if, on inspection, you decide it isn't actually your case.

  • Why can't you register the FailureAnalyzer as a @Bean or @Component?
    Analyzers run when the application context has already failed to start, so there are no live beans to look up. Boot instantiates them directly via SpringFactoriesLoader from spring.factories, independent of the context.
  • Your analyzer needs to read a property to build a better Action message. How do you get the Environment?
    Implement EnvironmentAware (or BeanFactoryAware for the bean factory). The diagnostics infrastructure injects them before calling analyze(). You cannot use @Autowired because no context exists.

saying these in an interview costs you the question

  • Registering the analyzer as a @Bean/@Component
  • Putting it in the AutoConfiguration.imports file
  • Trying to @Autowired dependencies into the analyzer
  • Typing AbstractFailureAnalyzer to a wrapper exception instead of the real cause

context

open as a page

What is a Spring Boot FailureAnalyzer, and what produces the "APPLICATION FAILED TO START" message you see when startup fails?

level: juniorimportance: should knowfreq 35%

basics

~20 s

A FailureAnalyzer turns a startup exception into a friendly error with a Description and an Action to fix it. Spring Boot prints these as the "APPLICATION FAILED TO START" banner instead of a raw stack trace.

open as a page

What does a FailureAnalysis contain, and how do built-in analyzers like the port-in-use and missing-bean analyzers use it?

level: middleimportance: should knowfreq 40%

basics

~20 s

FailureAnalysis holds three things: a description (what went wrong), an action (how to fix it), and the original cause. The port-in-use analyzer describes the busy port; the missing-bean analyzer names the required bean and suggests defining it.

open as a page

At what point in the SpringApplication lifecycle do FailureAnalyzers run, and why does that timing dictate how you get dependencies into them?

level: seniorimportance: nice to knowfreq 15%

basics

~10 s

They run after startup has already failed — when SpringApplication catches the fatal exception and reports it, before rethrowing. Because the context is dead, you can't autowire beans; you use EnvironmentAware/BeanFactoryAware injection instead.

open as a page

A team's custom FailureAnalyzer is never invoked — the app still prints a raw stack trace. What are the likely causes, and how do analyzer ordering and null returns factor in?

level: principalimportance: nice to knowfreq 12%

basics

~20 s

Common causes: registered in the wrong file (the AutoConfiguration.imports file instead of spring.factories), declared as a bean, wrong factory key, generic typed to a wrapper instead of the real exception, or analyze() returning null. Also, another analyzer may match first.

open as a page