StepExecutionListener can influence exit status but not status of step execution. The exit status can be downgraded from COMPLETE to FAILED by returning FAILED from the afterStep() callback. But the BatchStatus in the stepExecution is unchanged, so it looks as if the step was successful. It's a hard decision to decide how to fix this. I suggest that StepExecutionListener should be allowed to throw an exception to signal a failure, and treat that as a normal way for steps to fail, instead of trying to map exit codes to statuses.