In Spring Batch, what is the difference between BatchStatus and ExitStatus?
answer
- BatchStatus = enum, lifecycle, framework-owned
- ExitStatus = String code + description, extensible
- ExitStatus drives flow decisions
- ExitStatus derived from BatchStatus by default
- Both on JobExecution and StepExecution
basics
~20 sBatchStatus 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 sBatchStatus 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// 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 Stringgo deeper
Must know: BatchStatus = enum lifecycle, ExitStatus = String code, both present on executions.
Should explain the default derivation and that ExitStatus is the branching key.
Explains ownership (framework vs customizable), persistence columns, and independence of the two after customization.
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.