apache/dolphinscheduler · error · TaskExecutorRuntimeException

${this} has already started

Error message

${this} has already started

What it means

AbstractTaskExecutor.start is idempotence-guarded by a startFlag; invoking start() on a task executor that has already been started throws TaskExecutorRuntimeException. This prevents double initialization of task context and double publishing of the TaskRunning event.

Source

Thrown at dolphinscheduler-task-executor/src/main/java/org/apache/dolphinscheduler/task/executor/AbstractTaskExecutor.java:62

    protected final TaskExecutionContext taskExecutionContext;

    protected final TaskExecutorEventBus taskExecutorEventBus;

    public AbstractTaskExecutor(final TaskExecutionContext taskExecutionContext,
                                final TaskExecutorEventBus taskExecutorEventBus) {
        this.taskExecutionContext = taskExecutionContext;
        this.taskExecutorEventBus = taskExecutorEventBus;
        this.latestStateTrackTime = 0;
        this.startFlag = false;
        transitTaskExecutorState(TaskExecutorState.INITIALIZED);
    }

    @SneakyThrows
    @Override
    public void start() {
        if (startFlag) {
            throw new TaskExecutorRuntimeException(this + " has already started");
        }
        startFlag = true;
        TaskInstanceLogHeader.printInitializeTaskContextHeader();
        initializeTaskContext();

        publishTaskRunningEvent();

        TaskInstanceLogHeader.printLoadTaskInstancePluginHeader();
        initializeTaskPlugin();

        TaskInstanceLogHeader.printExecuteTaskHeader();

        if (taskExecutionContext.getDryRun() == Flag.YES.getCode()) {
            log.info("The task: {} is dryRun model, skip the trigger stage.", taskExecutionContext.getTaskName());
            transitTaskExecutorState(TaskExecutorState.SUCCEEDED);
            return;
        }

View on GitHub (pinned to 02eac45a1b)

Solutions

  1. Only call start() once per executor instance; create a fresh executor for a retry
  2. Check isStarted()/startFlag state before calling start()
  3. If restart is needed, build a new task executor instance from the task context

Example fix

// before
if (!taskExecutor.isStarted()) { taskExecutor.start(); }
taskExecutor.start(); // second call throws
// after
if (!taskExecutor.isStarted()) {
    taskExecutor.start();
}
Defensive patterns

Strategy: type-guard

Validate before calling

if (taskExecutor.isStarted()) {
    return; // or throw a controlled error at the call site
}
taskExecutor.start();

Type guard

boolean canStart(ITaskExecutor te) {
    return te != null && !te.isStarted();
}

Try / catch

try {
    taskExecutor.start();
} catch (TaskExecutorRuntimeException e) {
    if (!e.getMessage().contains("has already started")) throw e;
    // idempotent no-op: already started
}

Prevention

When it happens

Trigger: Calling start() twice on the same ITaskExecutor instance — e.g. container retry logic re-dispatching an executor that already began, or application code manually calling start after the container already started it.

Common situations: Buggy retry/kill-then-restart logic reusing the same executor object; duplicate event delivery causing a second start; framework code and user code both invoking start.

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/dolphinscheduler@02eac45a1b (2026-09-06). Data as JSON: /api/errors/628a0ec7c2d0bff5. Report an issue: GitHub.