skip to content

How does ApplicationFailedEvent work, and how would you use it (and the early listeners) to build robust startup diagnostics?

level: principalimportance: nice to knowfreq 20%

answer

  1. published on startup failure
  2. getException() + possibly-null context
  3. register as early listener, not @Component
  4. pair with ExitCodeGenerator for non-zero exit
  5. FailureAnalyzer = human message, event = machine hook

basics

~20 s

ApplicationFailedEvent is published when startup throws, carrying the exception and (if it got that far) the context. Because failures can occur before the context exists, listen with an early ApplicationListener registered outside the context so you can log/alert reliably.

solid answer

~50 s

ApplicationFailedEvent is published by SpringApplication (via the run listener's failed callback) whenever startup fails with an exception; it exposes getException() and the SpringApplication, and getApplicationContext() which may be null if the failure happened before the context was created. That nullability is the key design point: a robust diagnostics handler must not assume any beans or context exist. So you register the failure listener the same way as other early listeners — SpringApplication.addListeners(...) or META-INF/spring.factories under the ApplicationListener key — not as a @Component. In the handler you record structured diagnostics, emit an alert/metric, and optionally return a non-zero exit code via an ExitCodeGenerator/ExitCodeExceptionMapper. Combined with early listeners on ApplicationStartingEvent / ApplicationEnvironmentPreparedEvent you can capture phase timing and the environment snapshot so a failure report shows how far boot got. Boot's FailureAnalyzers provide the friendly console messages for known failures; ApplicationFailedEvent is your programmatic hook for alerting.

code

java · 25 lines
java
package com.example;
import org.springframework.boot.context.event.ApplicationFailedEvent;
import org.springframework.context.ApplicationListener;

public class StartupFailureReporter
        implements ApplicationListener<ApplicationFailedEvent> {
    @Override
    public void onApplicationEvent(ApplicationFailedEvent event) {
        try {
            Throwable cause = event.getException();
            boolean contextBuilt = event.getApplicationContext() != null; // may be null!
            // send structured alert; DO NOT call getBean() when context is null
            System.err.println("STARTUP FAILED (contextBuilt=" + contextBuilt + "): "
                + rootMessage(cause));
        } catch (RuntimeException handlerError) {
            handlerError.printStackTrace(); // never mask the original failure
        }
    }
    private static String rootMessage(Throwable t) {
        while (t.getCause() != null) t = t.getCause();
        return t.toString();
    }
}
// Register: new SpringApplication(App.class){{ addListeners(new StartupFailureReporter()); }}.run(args);
// or META-INF/spring.factories: org.springframework.context.ApplicationListener=com.example.StartupFailureReporter

go deeper

for a junior

Know it fires when the app fails to start and carries the exception.

for a middle

Register it as an early listener and read getException(); know Ready is skipped on failure.

for a senior

Handle the possibly-null context, wire exit codes, and distinguish it from FailureAnalyzer output.

for a principal

Design end-to-end startup diagnostics: phase breadcrumbs via early listeners, dependency-free alerting, exit-code strategy, and cause-chain handling.

## What ApplicationFailedEvent is `ApplicationFailedEvent` (`org.springframework.boot.context.event.ApplicationFailedEvent`) is published when `SpringApplication.run(...)` fails with an exception. It carries: - `getException()` — the `Throwable` that aborted startup. - `getApplicationContext()` — the `ConfigurableApplicationContext`, **which may be `null`** if the failure occurred before the context was created (e.g., an invalid property source during environment preparation). - the originating `SpringApplication` and args. Under the hood it is emitted by the run listener's `failed(context, throwable)` callback. ## Why the null-context detail dominates the design Because a failure can happen at *any* phase — including before the context exists — a diagnostics listener **cannot rely on beans, on the context, or on logging that depends on Spring-managed infrastructure**. Treat the handler like any early listener: self-contained, defensive about `null` context, using a bootstrap-safe logger. ## How to register the failure listener Same options as other pre-context listeners: 1. `SpringApplication.addListeners(new StartupFailureReporter())` before `run`. 2. `META-INF/spring.factories` under `org.springframework.context.ApplicationListener`. A `@Component`-based `@EventListener` is a trap: if the failure happens before the context is ready, your bean never gets a chance to handle it — exactly the case you care about. ## Building robust startup diagnostics (the principal-level design) - **Phase timing / breadcrumbs:** register early listeners on `ApplicationStartingEvent` and `ApplicationEnvironmentPreparedEvent` to record wall-clock checkpoints and an Environment snapshot (active profiles, key config). On failure you can report 'reached environment prep, failed during refresh'. - **Structured alerting:** in the `ApplicationFailedEvent` handler, serialize `getException()` (root cause chain), the last reached phase, and profiles; push to your alerting/metrics sink. Keep it dependency-free. - **Exit codes:** to make orchestrators (k8s, systemd) see the failure correctly, combine with an `ExitCodeGenerator` or `ExitCodeExceptionMapper` so the JVM exits non-zero with a meaningful code. `SpringApplication.exit(context, generators)` computes it. - **Don't fight FailureAnalyzers:** Boot's `FailureAnalyzer`/`FailureAnalysisReporter` produce the friendly, human-readable console messages for *known* failure types (port in use, bean not found). Those are for humans; `ApplicationFailedEvent` is your *machine* hook for alerting. Use both — they're complementary. ## Gotchas - **Null context** → NPE if you blindly call `getApplicationContext().getBean(...)`. - **Listener exceptions** → a throw inside the failure handler can mask the original cause; wrap handler body in try/catch and always log the original. - **Double handling** → registering both via spring.factories and addListeners fires it twice. - **Not all failures are exceptions you own** → the throwable may be a Boot wrapper; walk the cause chain. - **Shutdown vs failure** → `ContextClosedEvent` is normal shutdown of a started app; `ApplicationFailedEvent` is a *startup* failure. Don't conflate them. ## When to use Use `ApplicationFailedEvent` for programmatic startup-failure alerting, crash reporting, and exit-code control in production services — especially where you need to know a container failed to boot and *why*, before any logging framework tied to the context is available.

  • Why might event.getApplicationContext() be null in an ApplicationFailedEvent handler?
    Because startup can fail before the context is created — e.g., during environment preparation or context creation. The handler must be null-safe and not assume beans exist.
  • How do you ensure a failed startup returns a non-zero process exit code?
    Provide an ExitCodeGenerator (or ExitCodeExceptionMapper) so SpringApplication.exit computes a meaningful code, letting orchestrators like Kubernetes detect the failure.
  • How does ApplicationFailedEvent differ from Boot's FailureAnalyzer?
    FailureAnalyzer produces friendly human-readable console diagnostics for known failure types; ApplicationFailedEvent is a programmatic hook for machine-facing alerting/metrics. They're complementary.

saying these in an interview costs you the question

  • Assuming the context is always non-null in the failure handler
  • Registering the failure listener only as a @Component @EventListener
  • Confusing ApplicationFailedEvent (startup failure) with ContextClosedEvent (normal shutdown)
  • Throwing from the handler and masking the original exception

context