skip to content

BatchStatus & ExitStatus

BatchStatus records the lifecycle state while ExitStatus is a string code that drives flow transitions between steps. Keeping the two apart is the point, since only one of them can be customized to steer the flow.

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

explore

questions

5

In Spring Batch, what is the difference between BatchStatus and ExitStatus?

level: juniorimportance: must knowfreq 55%

answer

  1. BatchStatus = enum, lifecycle, framework-owned
  2. ExitStatus = String code + description, extensible
  3. ExitStatus drives flow decisions
  4. ExitStatus derived from BatchStatus by default
  5. Both on JobExecution and StepExecution

basics

~20 s

BatchStatus is an enum (COMPLETED, FAILED, STARTED, STOPPED...) describing a job's or step's lifecycle state. ExitStatus is a string-code object (exitCode plus description) used mainly to decide flow between steps. Both live on every JobExecution and StepExecution.

solid answer

~40 s

BatchStatus is a fixed enum recording the technical lifecycle of a JobExecution or StepExecution: STARTING, STARTED, COMPLETED, STOPPING, STOPPED, FAILED, ABANDONED, UNKNOWN. The framework owns it and uses it internally (for example to decide whether a job can restart). ExitStatus is a value object holding a free-form String exitCode plus an exitDescription; its default codes mirror BatchStatus ('COMPLETED', 'FAILED'...) but you can set any custom code. The key practical difference: ExitStatus is what step-to-step flow transitions match against, so you customize ExitStatus to branch a job, while BatchStatus stays a canonical, non-extensible record of what actually happened. Every JobExecution and StepExecution carries both, and by default ExitStatus is derived from BatchStatus.

code

java · 12 lines
java
// Both statuses live on every execution
StepExecution stepExecution = ...;

BatchStatus status = stepExecution.getStatus();        // enum, e.g. BatchStatus.COMPLETED
ExitStatus exit = stepExecution.getExitStatus();       // value object

String code = exit.getExitCode();                      // "COMPLETED" (a String)
String desc = exit.getExitDescription();               // free text / stack trace

// Enum vs String — this is the essence of the difference:
boolean unsuccessful = status.isUnsuccessful();        // BatchStatus knows lifecycle severity
boolean matchesBranch = "COMPLETED".equals(code);      // ExitStatus matched as a String

go deeper

for a junior

Must know: BatchStatus = enum lifecycle, ExitStatus = String code, both present on executions.

for a middle

Should explain the default derivation and that ExitStatus is the branching key.

for a senior

Explains ownership (framework vs customizable), persistence columns, and independence of the two after customization.

for a principal

Frames the design rationale: closed canonical record vs open extensible outcome code.

## The two concepts Every `JobExecution` and every `StepExecution` in Spring Batch carries **two** status fields, and candidates constantly confuse them. ### BatchStatus `BatchStatus` is a Java **enum** in `org.springframework.batch.core`. Its values are: `COMPLETED`, `STARTING`, `STARTED`, `STOPPING`, `STOPPED`, `FAILED`, `ABANDONED`, `UNKNOWN`. Because it is an enum, it is a **closed, fixed set** — you cannot add your own value. It represents the **technical lifecycle state** of the execution and is owned and managed by the framework. It is what the `JobRepository` persists in the `BATCH_JOB_EXECUTION.STATUS` / `BATCH_STEP_EXECUTION.STATUS` columns, and it is what the framework consults to answer questions like *'is this execution still running?'* or *'may this job instance be restarted?'*. ### ExitStatus `ExitStatus` is **not an enum** — it is a plain **value object** (a class implementing `Comparable` and `Serializable`) with two important fields: - `exitCode` — a **String** (e.g. `"COMPLETED"`, `"FAILED"`, or anything you invent like `"COMPLETED_WITH_SKIPS"`) - `exitDescription` — a free-text String, typically a stack trace or human message. Spring ships constants for the common codes: `ExitStatus.COMPLETED`, `ExitStatus.FAILED`, `ExitStatus.STOPPED`, `ExitStatus.EXECUTING`, `ExitStatus.NOOP`, `ExitStatus.UNKNOWN`. Because the code is just a String, `ExitStatus` is **open/extensible** — that extensibility is the whole point. ## Why two? BatchStatus answers *'what technically happened to this execution?'* (a canonical record). ExitStatus answers *'what outcome code should influence what happens next?'* Flow decisions (which step runs next) are driven by the **ExitStatus string**, not by BatchStatus, precisely because you may want several distinct outcomes that all still map to `BatchStatus.COMPLETED` at the framework level. ## Default relationship At the end of a step or job, if you do not customize anything, the ExitStatus **is derived from** the BatchStatus: a step that ends `BatchStatus.COMPLETED` gets `ExitStatus.COMPLETED`, a `FAILED` BatchStatus yields `ExitStatus.FAILED`, and so on. So out of the box the two agree. ## Gotchas - ExitStatus is compared by `exitCode` String — case and exact spelling matter. - Customizing ExitStatus (e.g. in an `afterStep` listener) does **not** change the BatchStatus; the framework still records COMPLETED/FAILED independently. - ExitStatus has severity ordering via `and(...)`/`Comparable`; BatchStatus has its own severity via `max(...)`. They are separate mechanisms. ## When to use which - Read/inspect **BatchStatus** when you want the authoritative lifecycle outcome (monitoring, restart logic). - Set/customize **ExitStatus** when you want to influence branching or expose a finer-grained outcome code.

  • If you change a step's ExitStatus, does the BatchStatus change too?
    No. BatchStatus is recorded independently by the framework. Customizing ExitStatus (e.g. returning a new ExitStatus from afterStep) changes only the exit code used for branching/reporting; the persisted BatchStatus still reflects the technical outcome (COMPLETED/FAILED).
  • Which of the two would you match on to route a job down a different path?
    ExitStatus. Flow transitions compare the ExitStatus exitCode String, so custom codes let you branch. BatchStatus is a closed enum and is not meant for custom branching keys.

saying these in an interview costs you the question

  • Saying both are enums (ExitStatus is NOT an enum — it's a String-code value object).
  • Claiming ExitStatus and BatchStatus are always identical (they agree by default but ExitStatus can be customized).
  • Thinking BatchStatus can be extended with custom values.

context

open as a page

How is a step's ExitStatus derived from its BatchStatus by default, and how do you customize ExitStatus?

level: middleimportance: must knowfreq 45%

basics

~10 s

By default the ExitStatus mirrors the BatchStatus (COMPLETED->'COMPLETED', FAILED->'FAILED'). To customize, return a different ExitStatus from a StepExecutionListener's afterStep method (or set it on the StepExecution). The BatchStatus itself stays unchanged.

open as a page

How does BatchStatus severity ordering work, and what do max(), isRunning(), and isUnsuccessful() do?

level: seniorimportance: should knowfreq 30%

basics

~20 s

BatchStatus values are ordered by severity. BatchStatus.max(a, b) returns the more severe of two statuses — used to aggregate a job's status from its steps. isRunning() is true for STARTING/STARTED; isUnsuccessful() is true for FAILED (and more severe).

open as a page

Where are BatchStatus and ExitStatus persisted, and what practical problems does storing them separately solve?

level: seniorimportance: should knowfreq 22%

basics

~20 s

The JobRepository stores both in the batch metadata tables: a STATUS column holds the BatchStatus name, and an EXIT_CODE/EXIT_MESSAGE column holds the ExitStatus. Keeping them separate lets you record the canonical lifecycle and a flexible outcome code independently.

open as a page

Why does Spring Batch model outcome as two separate concepts (a closed BatchStatus enum and an open ExitStatus code) instead of one?

level: principalimportance: nice to knowfreq 15%

basics

~20 s

Because two different consumers need different vocabularies: the framework needs a small, fixed, trustworthy lifecycle enum for restart and monitoring, while application flows need an open, extensible outcome code for branching and reporting. One field can't safely serve both.

open as a page