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
- Only call start() once per executor instance; create a fresh executor for a retry
- Check isStarted()/startFlag state before calling start()
- 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
- Treat start() as one-shot; guard with an isStarted check
- Never reuse an executor instance for retries — build a new one
- Route all starts through the container rather than calling start manually
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.