Skip to content

Propagate step exit description from FlowJob to JobExecution#5430

Open
seonwooj0810 wants to merge 1 commit into
spring-projects:mainfrom
seonwooj0810:fix/issue-3740-flow-exit-description
Open

Propagate step exit description from FlowJob to JobExecution#5430
seonwooj0810 wants to merge 1 commit into
spring-projects:mainfrom
seonwooj0810:fix/issue-3740-flow-exit-description

Conversation

@seonwooj0810

Copy link
Copy Markdown
Contributor

Fixes #3740

Root cause

When a step inside a FlowJob fails, the StepExecution's exit status already contains the failure description (typically the stack trace appended by AbstractStep.execute() via ExitStatus.addExitDescription(ex)). SimpleJob propagates that description to the JobExecution directly in doExecute():

execution.setExitStatus(stepExecution.getExitStatus());

FlowJob does not. Instead, it relies on JobFlowExecutor.updateJobExecutionStatus(FlowExecutionStatus), which only carries the final flow status name:

public void updateJobExecutionStatus(FlowExecutionStatus status) {
    execution.setStatus(findBatchStatus(status));
    exitStatus = exitStatus.and(new ExitStatus(status.getName()));
    execution.setExitStatus(exitStatus);
}

executeStep previously returned only the step's exit code and discarded its exit description. The executor's tracked exitStatus therefore stayed at ExitStatus.EXECUTING (description "") until it was merged with new ExitStatus(status.getName()) — also without a description. The result: when a FlowJob ends in FAILED, the resulting JobExecution.getExitStatus().getExitDescription() is empty, even though the failing step has the exception detail. Tooling and listeners that inspect the job-level exit description (e.g. job-level logging, downstream notification jobs) lose the cause.

Change

In JobFlowExecutor.executeStep, accumulate the just-executed step's exit description into the executor's tracked ExitStatus via ExitStatus.addExitDescription. The subsequent updateJobExecutionStatus then merges the description through to the JobExecution.

ExitStatus.addExitDescription already:

  • returns this when the new description is blank, so successful steps with empty descriptions cost nothing;
  • deduplicates equal descriptions, so the same failure is not duplicated;
  • concatenates distinct descriptions with ; , so multiple failing steps along a flow path are preserved without losing earlier context.

This mirrors SimpleJob's behavior while staying compatible with arbitrary flow shapes — including transitions, decisions, and split flows where multiple step executions may carry descriptions.

Test evidence

Added FlowJobFailureTests#testStepFailureExitDescriptionPropagatedToJobExecution. The test installs a step that sets ExitStatus.FAILED.addExitDescription("boom: simulated step failure") and asserts that the resulting JobExecution.getExitStatus().getExitDescription() contains that text.

  • Without the fix: the assertion fails because the description is empty ('').
  • With the fix: passes.

All existing tests in org.springframework.batch.core.job.flow.** and org.springframework.batch.core.job.** continue to pass:

mvnw -pl spring-batch-core test \
     -Dtest='org.springframework.batch.core.job.**'
[INFO] Tests run: 150, Failures: 0, Errors: 0, Skipped: 42
[INFO] BUILD SUCCESS

(Skipped tests are pre-existing infrastructure-dependent tests.)

Verification done

  1. Confirmed no in-flight PR — gh pr list --search "FlowJob ExitDescription" and gh pr list --search "JobFlowExecutor updateJobExecutionStatus" both empty.
  2. No claim comments in the issue thread (zero comments since 2020-07-08).
  3. Fix touches .java files only.
  4. Verified the bug pattern still exists on current main (d8447d8bc) by grepping JobFlowExecutor.updateJobExecutionStatus and inspecting executeStep.
  5. Verified the bug behavior by running the new test against unmodified JobFlowExecutor (fails with empty description) and against the patched version (passes).
  6. Issue thread acknowledges the asymmetry between SimpleJob.doExecute() and JobFlowExecutor.updateJobExecutionStatus() — the description here matches the analysis the issue author already laid out.

Issue: spring-projectsgh-3740

When a step inside a FlowJob fails, the StepExecution's exit status
already contains the failure description (typically the stack trace
recorded by AbstractStep). However, JobFlowExecutor.executeStep only
returned the exit code, and the final JobFlowExecutor.updateJobExecutionStatus
combined the executor's tracked ExitStatus (which had no description)
with an ExitStatus built from the FlowExecutionStatus name. As a result
the step's exit description was discarded and the JobExecution ended
with an empty exit description, in contrast to SimpleJob which assigns
stepExecution.getExitStatus() directly.

Accumulate the step's exit description into the executor's tracked
ExitStatus in executeStep so it survives the final
updateJobExecutionStatus call. ExitStatus.addExitDescription already
deduplicates equal descriptions and concatenates distinct ones with a
semicolon, so multiple step failures along a flow path are preserved
without duplication.

Signed-off-by: Seonwoo Jung <laborlawseon@kap.kr>
@seonwooj0810 seonwooj0810 force-pushed the fix/issue-3740-flow-exit-description branch from 468d5f9 to c4f740f Compare June 23, 2026 07:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

FlowJob implementation doesn't set the exception message in JobExecution.ExitStatus.ExitDescription when exception is thrown

1 participant