apache/seatunnel · error · SeaTunnelEngineException

Job is trying to leave terminal state ${current}

Error message

Job is trying to leave terminal state ${current}

What it means

SeaTunnel Zeta engine throws this when a job state transition attempts to move a PhysicalPlan out of a terminal state (FINISHED, FAILED, CANCELED, etc.). Terminal states are final by design; the plan guards the transition inside updateJobState so that no stale callback or late state update can resurrect a finished job. It is a SeaTunnelEngineException, so it usually surfaces during coordinator operations like cancel or savepoint racing with job completion.

Source

Thrown at seatunnel-engine/seatunnel-engine-server/src/main/java/org/apache/seatunnel/engine/server/dag/physical/PhysicalPlan.java:334

                log.warn(
                        "{} current state is null, skip transition to {}",
                        jobFullName,
                        targetState);
                return;
            }
            log.debug(
                    "Try to update the {} state from {} to {}", jobFullName, current, targetState);

            if (current.equals(targetState)) {
                log.info(
                        "{} current state equals target state: {}, skip", jobFullName, targetState);
                return;
            }

            // consistency check
            if (current.isEndState()) {
                String message = "Job is trying to leave terminal state " + current;
                throw new SeaTunnelEngineException(message);
            }

            // Now do the actual state transition, we must update runningJobStateTimestampsIMap
            // first and then can update runningJobStateIMap
            updateStateInfo(current, targetState);
            reportJobStateEvent(targetState);

            stateProcess();
        } catch (Exception e) {
            log.error(ExceptionUtils.getMessage(e));
            if (!targetState.equals(JobStatus.FAILING)) {
                makeJobFailing(e);
            }
        }
    }

    public JobImmutableInformation getJobImmutableInformation() {
        return jobImmutableInformation;

View on GitHub (pinned to cf67b549a7)

Solutions

  1. Check the job's current state via REST/CLI before issuing cancel, savepoint, or stop operations
  2. Treat the exception as confirmation that the job already completed; query the final JobResult instead of retrying the transition
  3. Fix client code to subscribe to job state events rather than polling and issuing duplicate lifecycle calls
  4. If it occurs during cluster restart/failover replay, verify the runningJobStateIMap is consistent and no duplicated callbacks are registered

Example fix

// before
if (jobMetrics.someCondition()) {
    jobClient.savepoint(); // may race with completion
}
// after
JobStatus s = jobClient.getJobStatus();
if (!s.isEndState()) {
    jobClient.savepoint();
}
Defensive patterns

Strategy: try-catch

Validate before calling

JobStatus s = jobClient.getJobStatus(jobId);
if (s.isEndState()) { /* skip cancel/savepoint/stop */ }

Type guard

boolean isTerminal(JobStatus s) { return s.isEndState(); }

Try / catch

try { jobClient.cancelJob(jobId); } catch (SeaTunnelEngineException e) { if (e.getMessage().contains("leave terminal state")) { /* job already finished; treat as no-op */ } else { throw e; } }

Prevention

When it happens

Trigger: Calling cancelJob/savepointJob/stopJob after the job already reached an end state; a pipeline-end callback firing after the plan was marked terminal; startJob on a plan whose state was already finalized by a prior run; concurrent state updates landing after terminal transition.

Common situations: User issues a cancel at the same moment the job finishes; retry/restart logic re-invokes startJob on an already-terminal plan; savepoint requested on a completed job; race between master node failover replay and job completion events.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of apache/seatunnel@cf67b549a7 (2026-09-10). Data as JSON: /api/errors/7365c43efbc329d1. Report an issue: GitHub.