kestra-io/kestra · error · IllegalStateException

Execution must be terminated or paused and not killed to be

Error message

Execution must be terminated or paused and not killed to be restarted, current state is '{execution.getState().getCurrent()}' !

What it means

The `ExecutionService.restart()` method checks `execution.getState().canBeRestarted()` before proceeding. Only executions in a terminal state (SUCCESS, FAILED, WARNING, etc.) or PAUSED state — and not KILLED — can be restarted. If the execution is still RUNNING, CREATED, QUEUED, or has been KILLED, an `IllegalStateException` is thrown. Restarting re-runs the execution from a specific task.

Source

Thrown at core/src/main/java/io/kestra/core/services/ExecutionService.java:283

        if (createCommand.variables() != null) {
            newExecution = newExecution.withVariables(createCommand.variables());
        }

        /*
         * if (emitEvent) {
         * eventPublisher.publishEvent(CrudEvent.create(newExecution));
         * }
         */
        return newExecution;
    }

    public Execution restart(final Execution execution, Flow flow, @Nullable Integer revision) throws Exception {
        return restart(execution, flow, revision, false);
    }

    public Execution restart(final Execution execution, Flow flow, @Nullable Integer revision, boolean emitEvent) throws Exception {
        if (!execution.getState().canBeRestarted()) {
            throw new IllegalStateException(
                "Execution must be terminated or paused and not killed to be restarted, " +
                    "current state is '" + execution.getState().getCurrent() + "' !"
            );
        }

        final String newExecutionId = revision != null ? IdUtils.create() : null;

        // When an execution has no task runs (e.g., failed due to concurrency limit exceeded
        // before any task ran), restart it as a fresh child execution with an empty task-run list.
        if (ListUtils.isEmpty(execution.getTaskRunList())) {
            List<Label> newLabels = new ArrayList<>(ListUtils.emptyOnNull(execution.getLabels()));
            if (!newLabels.contains(new Label(Label.RESTARTED, "true"))) {
                newLabels.add(new Label(Label.RESTARTED, "true"));
            }
            Execution newExecution = execution
                .childExecution(newExecutionId, Collections.emptyList(), execution.withState(State.Type.RESTARTED).getState())
                .withMetadata(execution.getMetadata().nextAttempt())
                .withLabels(newLabels);

View on GitHub (pinned to 823fada927)

Solutions

  1. Wait for the execution to reach a terminal state (SUCCESS, FAILED, WARNING) before restarting.
  2. Killed executions cannot be restarted — trigger a new execution instead.
  3. Refresh the execution status in the UI before clicking Restart.
  4. For PAUSED executions, restart is allowed but should be used intentionally (it resumes from the restart point).

Example fix

# before: restart a running execution
POST /api/v1/executions/{runningExecutionId}/restart
# after: wait for terminal state first
# 1. Check execution state
GET /api/v1/executions/{executionId}
# 2. Only restart when state is FAILED, SUCCESS, WARNING, or PAUSED
POST /api/v1/executions/{executionId}/restart
Defensive patterns

Strategy: validation

Validate before calling

// Check restartability before calling restart
Execution execution = executionRepository.findById(tenantId, executionId)
    .orElseThrow(() -> new NoSuchElementException("Execution not found"));
if (!execution.getState().canBeRestarted()) {
    throw new IllegalStateException(
        "Cannot restart execution in state " + execution.getState().getCurrent()
            + ". Must be terminated or paused, and not killed.");
}
Execution restarted = executionService.restart(execution, flow, revision);

Type guard

const TERMINAL_STATES: Set<string> = new Set(['SUCCESS', 'FAILED', 'WARNING', 'PAUSED', 'CANCELLED']);

function canBeRestarted(state: string): boolean {
    return TERMINAL_STATES.has(state) && state !== 'KILLED';
}

Try / catch

try {
    Execution restarted = executionService.restart(execution, flow, revision);
} catch (IllegalStateException e) {
    if (e.getMessage().contains("must be terminated or paused")) {
        log.warn("Cannot restart execution {} in state {}: {}",
            execution.getId(), execution.getState().getCurrent(), e.getMessage());
        // suggest: wait for terminal state, or trigger a new execution if killed
        throw e;
    }
    throw e;
}

Prevention

When it happens

Trigger: Calling `POST /executions/{id}/restart` on an execution that is currently RUNNING. Restarting an execution that was KILLED (killed executions cannot be restarted). Restarting an execution that is in a non-terminal, non-paused state like CREATED or QUEUED.

Common situations: A user attempts to restart an execution that is still in progress. A flow was killed by the user or system and the user tries to restart it. The execution state changed between the UI display and the API call.

Related errors


AI-assisted analysis of kestra-io/kestra@823fada927 (2026-08-14). Data as JSON: /api/errors/74cf222ccb8e3486. Report an issue: GitHub.