apache/dolphinscheduler · error · IllegalStateException
"InternalThread is already started"
Error message
"InternalThread is already started"
What it means
Second guard in WorkflowSerialCoordinator.start(): if internalThread is already created, the coordinator's background thread was started and start() cannot run again. Distinct from the flag check to catch direct thread mutation.
Source
Thrown at dolphinscheduler-master/src/main/java/org/apache/dolphinscheduler/server/master/engine/workflow/serial/WorkflowSerialCoordinator.java:83
@Autowired
private SerialCommandPriorityHandler serialCommandPriorityHandler;
private volatile boolean flag = false;
private Thread internalThread;
private static final int DEFAULT_FETCH_SIZE = 1000;
private static final int DEFAULT_FETCH_INTERVAL_SECONDS = 5;
@Override
public synchronized void start() {
log.info("WorkflowSerialCoordinator starting...");
if (flag) {
throw new IllegalStateException("WorkflowSerialCoordinator is already started");
}
if (internalThread != null) {
throw new IllegalStateException("InternalThread is already started");
}
flag = true;
internalThread = new BaseDaemonThread(this::doStart) {
};
internalThread.setName("WorkflowSerialCoordinator-Thread");
internalThread.start();
log.info("WorkflowSerialCoordinator started...");
}
private void doStart() {
while (flag) {
try {
final StopWatch workflowSerialCoordinatorRoundCost = StopWatch.createStarted();
final List<SerialCommandsGroup> serialCommandsGroups = fetchSerialCommands();
serialCommandsGroups.forEach(this::handleSerialCommand);
log.debug("WorkflowSerialCoordinator handled SerialCommandsGroup size: {}, cost: {}/ms ",
serialCommandsGroups.size(),
workflowSerialCoordinatorRoundCost.getDuration().toMillis());View on GitHub (pinned to 02eac45a1b)
Solutions
- Only start the coordinator once per process lifetime
- Recreate the coordinator object instead of re-calling start()
- Remove duplicate start invocations from lifecycle code
- Reset internalThread=null only after a proper close()/stop()
Example fix
// before internalThread = null; coordinator.start(); // restart attempt // after coordinator.close(); WorkflowSerialCoordinator fresh = new WorkflowSerialCoordinator(...); fresh.start();
Defensive patterns
Strategy: validation
Validate before calling
if (coordinator.isStarted()) { log.warn("already started"); return; } Try / catch
try { coordinator.start(); } catch (IllegalStateException e) { log.warn("start ignored: {}", e.getMessage()); } Prevention
- Guard start calls with an isStarted flag
- Perform proper stop()/close() before any restart
- Don't null out internalThread manually
When it happens
Trigger: Repeated start() calls where the flag may have been reset but internalThread is non-null; double initialization of the coordinator.
Common situations: Same as double-start: duplicate lifecycle hooks, tests reusing an instance, manual restart attempts.
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
- "WorkflowSerialCoordinator is already started"
- TaskExecutionContextCreateException(ex.getMessage())
- CommandDuplicateHandleException(command)
- "The post SerialCommand except WAITING state but -> " + seri
- "SerialCommand with ExecutionType=PARALLEL is not supported,
AI-assisted analysis of apache/dolphinscheduler@02eac45a1b (2026-09-06).
Data as JSON: /api/errors/0dc00e9b87d8b993.
Report an issue: GitHub.