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?
answer
- spring.factories NOT AutoConfiguration.imports
- not a bean; SpringFactoriesLoader loads it
- generic type must match real cause in chain
- first non-null FailureAnalysis wins; @Order to prioritize
- runtime exceptions bypass diagnostics
basics
~20 sCommon 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.
solid answer
~40 sWalk the failure modes in order of likelihood. First, **registration location**: `FailureAnalyzer`s go in `META-INF/spring.factories` under `org.springframework.boot.diagnostics.FailureAnalyzer` — putting them in the Boot 3 `AutoConfiguration.imports` file, or annotating them `@Component`, means they're never loaded. Second, **type mismatch**: `AbstractFailureAnalyzer<T>` only fires when `T` appears in the exception's cause chain; if you typed it to a Spring wrapper (`BeanCreationException`) rather than your real cause, it may not match as expected. Third, **null returns**: if your `analyze()` conditionally returns `null`, it declines the failure. Fourth, **ordering / first-match-wins**: analyzers are consulted in registration/`@Order` order and the first non-null `FailureAnalysis` wins, so a broader built-in analyzer could claim the exception before yours. Finally, verify the exception actually surfaces at startup — runtime exceptions bypass diagnostics entirely.
go deeper
Know that wrong registration means the analyzer is silently ignored.
Check spring.factories key, bean-vs-factory registration, and null returns.
Add generic-type-matching and first-match-wins ordering to the diagnosis.
Systematically enumerate all failure modes and prescribe library-author practices: precise types, @Order precedence, pure analyze(), and per-analyzer unit tests.
## Debugging checklist for "my analyzer never runs" ### 1. Wrong registration file (the #1 mistake) Spring Boot 2.7/3.x introduced `META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports` — but **only** for auto-configuration classes. `FailureAnalyzer` registration was **not** migrated; it still lives in **`META-INF/spring.factories`**: ```properties org.springframework.boot.diagnostics.FailureAnalyzer=\ com.example.MyFailureAnalyzer ``` Symptoms of getting this wrong: the analyzer compiles, tests of the class pass, but at runtime it's simply never discovered. Also check the key spelling exactly — a typo in the fully-qualified key silently disables discovery. ### 2. Declared as a bean If someone slapped `@Component`/`@Bean` on it (perhaps to get injection), it won't be used for diagnostics — analyzers are loaded by `SpringFactoriesLoader`, not the context. Remove the stereotype and register in `spring.factories`. ### 3. Generic type points at the wrong throwable `AbstractFailureAnalyzer<T>` scans the cause chain for a throwable assignable to `T`. If the real cause is `MyException` but you wrote `AbstractFailureAnalyzer<IllegalStateException>` or targeted a wrapper, matching behavior won't be what you intend. Target the **most specific** exception you control. ### 4. analyze() returns null Returning `null` is a legitimate "not mine" signal, but an over-eager guard clause can make the analyzer decline the very case it was written for. Confirm the path that should produce a `FailureAnalysis` actually does. ### 5. Ordering and first-match-wins The diagnostics runner asks each analyzer in turn and stops at the **first non-null** `FailureAnalysis`. Analyzer order follows `SpringFactoriesLoader` order, adjustable with `@Order` / `Ordered` (lower value = higher priority). If a broad built-in analyzer (say, one matching a wrapper exception higher in the chain) returns first, your more specific message never appears. Give your analyzer higher precedence with `@Order(Ordered.HIGHEST_PRECEDENCE)` if it must win. ### 6. The exception isn't a startup failure at all Diagnostics only runs on the `SpringApplication` startup-failure path. If your exception is thrown while handling a request (post-startup), no analyzer runs — you'd use `@ControllerAdvice`/`@ExceptionHandler` for that instead. ### 7. It's masked by a different reporter or the exception is swallowed If the code catches your exception and recovers (so refresh succeeds), there's no fatal failure to analyze. Ensure the exception actually propagates out of context refresh. ## Design guidance for library authors When shipping a starter, bundle analyzers for the misconfigurations your users will hit, type them precisely, keep `analyze()` pure, write a message with a genuinely actionable Action, and register them in the starter's own `spring.factories`. Unit-test each analyzer by constructing the exception and asserting on description/action — that catches null-return regressions. ## Summary of the two mechanisms to keep straight - **Auto-configuration** → `META-INF/spring/...AutoConfiguration.imports`. - **FailureAnalyzer** → `META-INF/spring.factories`, key `org.springframework.boot.diagnostics.FailureAnalyzer`.
- Two analyzers can both explain the same exception. How do you guarantee yours produces the message?Analyzers are consulted in order and the first non-null FailureAnalysis wins. Raise your analyzer's precedence by implementing Ordered / using @Order (lower value = earlier), e.g. Ordered.HIGHEST_PRECEDENCE, so it's asked before the broader built-in one.
- How would you unit-test that a FailureAnalyzer produces the right message?Instantiate the analyzer, construct your exception, call analyze() (directly or via the interface method wrapping it in a throwable chain), and assert on FailureAnalysis.getDescription() and getAction(). No Spring context is needed, which also catches accidental null returns.
saying these in an interview costs you the question
- Believing FailureAnalyzer uses the AutoConfiguration.imports file
- Thinking all matching analyzers' messages are combined
- Assuming analyzer order is random rather than Ordered-driven
- Expecting diagnostics to run for post-startup runtime exceptions