skip to content

What checked exceptions can JobLauncher.run() throw, and what do they tell you about job identity and restart?

level: seniorimportance: should knowfreq 45%

answer

  1. 4 checked: AlreadyRunning, Restart, InstanceAlreadyComplete, ParametersInvalid
  2. thrown at launch, not for a FAILED run
  3. identity = name + identifying params
  4. instance completes once
  5. RunIdIncrementer / vary a param to fix

basics

~10 s

run() throws four checked exceptions: JobExecutionAlreadyRunningException, JobRestartException, JobInstanceAlreadyCompleteException, and JobParametersInvalidException. They signal that the run couldn't even start because of the job's identity, state, or bad parameters.

solid answer

~40 s

run(job, params) declares four checked exceptions, all thrown before/at launch — they mean the launcher refused to start this attempt, distinct from a job that starts and then FAILs. JobParametersInvalidException: the parameters didn't pass validation (e.g. a JobParametersValidator or missing required key). JobInstanceAlreadyCompleteException: a JobInstance with these identifying parameters already completed successfully and the job isn't restartable/incrementing — you must vary an identifying parameter. JobExecutionAlreadyRunningException: an execution for this instance is currently running — no concurrent duplicate launch. JobRestartException: a restart was attempted but isn't valid (e.g. restarting an already-complete or non-restartable job). All four flow from JobParameters defining JobInstance identity, and from Batch's rule that a JobInstance runs to success exactly once. The practical fixes: use RunIdIncrementer or a unique parameter, validate inputs, and don't relaunch a running instance.

code

java · 13 lines
java
try {
    JobExecution exec = jobLauncher.run(importJob, params);
    // A started-then-failed job does NOT throw; check status instead:
    if (exec.getStatus() == BatchStatus.FAILED) { /* handle failure */ }
} catch (JobInstanceAlreadyCompleteException e) {
    // same identifying params already succeeded -> vary a param / use RunIdIncrementer
} catch (JobExecutionAlreadyRunningException e) {
    // an execution for this instance is still running -> reject (e.g. HTTP 409)
} catch (JobRestartException e) {
    // restart not allowed (complete or preventRestart())
} catch (JobParametersInvalidException e) {
    // parameters failed the JobParametersValidator
}

go deeper

for a junior

Aware run() can throw if you relaunch an already-completed job with the same parameters.

for a middle

Name at least JobInstanceAlreadyCompleteException and the RunIdIncrementer fix.

for a senior

Enumerate all four, distinguish launch-time exceptions from a FAILED status, and map them to the identity model.

for a principal

Design idempotent/retriable launch APIs: map exceptions to HTTP semantics, decide restart-vs-new-instance policy, and choose validators/incrementers deliberately.

## The four checked exceptions `JobLauncher.run(Job, JobParameters)` declares: ```java JobExecution run(Job job, JobParameters params) throws JobExecutionAlreadyRunningException, JobRestartException, JobInstanceAlreadyCompleteException, JobParametersInvalidException; ``` All are thrown **at launch time** — the run never reaches STARTED. This is a key distinction: a job that *starts and then fails* does **not** throw; instead `run()` returns normally with a `JobExecution` whose `BatchStatus` is `FAILED`. These exceptions mean 'I refused to launch.' ### 1. JobParametersInvalidException The supplied `JobParameters` failed validation. A `Job` can carry a `JobParametersValidator` (e.g. `DefaultJobParametersValidator` requiring/forbidding specific keys). If required keys are missing or forbidden ones present, the launcher throws this before doing anything. **Fix:** pass the correct parameter set. ### 2. JobInstanceAlreadyCompleteException A `JobInstance` is identified by the job name + its **identifying** `JobParameters`. Spring Batch's core rule: **a JobInstance may complete successfully only once.** If you launch with an identifying-parameter set whose instance already reached `COMPLETED`, and the job doesn't produce a *new* instance, the launcher throws this. **Fix:** change an identifying parameter — add a `run.id`/timestamp, or put a `RunIdIncrementer`/custom `JobParametersIncrementer` on the job so each launch is a fresh instance. ### 3. JobExecutionAlreadyRunningException There is **already a JobExecution in a running state** (STARTED/STARTING) for that JobInstance. Batch forbids two concurrent executions of the same instance. **Fix:** wait for it to finish, or launch a different instance (different identifying params). ### 4. JobRestartException A **restart** was attempted but is invalid — e.g. the instance already completed, or the job is marked non-restartable (`JobBuilder#preventRestart()`), or the previous execution left inconsistent restart state. A restart happens when you relaunch with the **same** identifying parameters after a `FAILED`/`STOPPED` execution — Batch continues from where it left off. This exception says that continuation isn't allowed. **Fix:** don't restart a non-restartable/complete job; start a new instance instead. ## The identity model underneath These exceptions all trace back to two invariants: 1. **Identity = job name + identifying JobParameters** => the `JobInstance`. 2. **A JobInstance succeeds exactly once**, and only one execution runs at a time. So whether you can (re)launch depends on the prior `JobExecution`s recorded in the `JobRepository`: - No prior execution => new run. - Prior FAILED/STOPPED, restartable => **restart** (same params, continues). - Prior COMPLETED => `JobInstanceAlreadyCompleteException` unless params change. - Currently running => `JobExecutionAlreadyRunningException`. ## Practical patterns - **RunIdIncrementer**: `new JobBuilder(name, repo).incrementer(new RunIdIncrementer())...` adds/bumps a `run.id` parameter each launch, guaranteeing a new instance — used when every launch should be independent. - **Deliberate restart**: keep identifying parameters *stable* (e.g. a business date) so a failed run can be restarted and resume incomplete steps; use `preventRestart()` only when resuming would be unsafe. - **Validation up front**: attach a `DefaultJobParametersValidator` with required/optional keys to fail fast with `JobParametersInvalidException` rather than deep inside a step. ## Gotchas - Catching these as a generic `Exception` and logging 'job failed' is misleading — the job never ran. Handle them distinctly (often a 409/conflict at an API layer). - Non-identifying parameters (`addLong("key", val, false)`) do **not** affect instance identity, so they won't dodge `JobInstanceAlreadyCompleteException`. - On startup, `JobLauncherApplicationRunner` surfaces these too — a second boot with the same params after success throws `JobInstanceAlreadyCompleteException`.

  • Does run() throw when a job starts and then fails partway through?
    No. Those four exceptions are launch-time only. A job that starts and fails returns a JobExecution with BatchStatus.FAILED — you inspect the status, no exception is thrown.
  • How do you make repeated launches with 'the same' inputs always succeed?
    Add a RunIdIncrementer (or a unique identifying parameter like a timestamp) so each launch is a new JobInstance, avoiding JobInstanceAlreadyCompleteException.
  • What's the difference between restarting and starting a new instance?
    Restart = relaunch with the same identifying params after a FAILED/STOPPED run; it resumes incomplete steps. New instance = different identifying params; it starts fresh.

saying these in an interview costs you the question

  • Believing run() throws when a running job fails mid-way (it returns FAILED).
  • Thinking non-identifying parameters change instance identity.
  • Not knowing a JobInstance can complete successfully only once.
  • Treating all four exceptions as generic 'job error'.

context