apache/dolphinscheduler · warning · ServiceException

The workflow instance: %s state is %s, cannot recovery

Error message

The workflow instance: %s state is %s, cannot recovery

What it means

Raised in RecoverSuspendedWorkflowInstanceExecutorDelegate.execute when the workflow instance fetched from the registry is not in a suspended/paused state. Recovery-from-suspend only applies to suspended instances; any other state (running, finished, failed, killed) makes recovery invalid, so the executor refuses with the instance id and its current state.

Source

Thrown at dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/executor/workflow/RecoverSuspendedWorkflowInstanceExecutorDelegate.java:46

import org.apache.dolphinscheduler.registry.api.RegistryClient;
import org.apache.dolphinscheduler.registry.api.enums.RegistryNodeType;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class RecoverSuspendedWorkflowInstanceExecutorDelegate
        implements
            IExecutorDelegate<RecoverSuspendedWorkflowInstanceExecutorDelegate.RecoverSuspendedWorkflowInstanceOperation, Void> {

    @Autowired
    private RegistryClient registryClient;

    @Override
    public Void execute(RecoverSuspendedWorkflowInstanceOperation workflowInstanceControlRequest) {
        final WorkflowInstance workflowInstance = workflowInstanceControlRequest.workflowInstance;
        if (!workflowInstance.getState().isPaused() && !workflowInstance.getState().isStopped()) {
            throw new ServiceException(
                    String.format("The workflow instance: %s state is %s, cannot recovery", workflowInstance.getName(),
                            workflowInstance.getState()));
        }
        final Server masterServer = registryClient.getRandomServer(RegistryNodeType.MASTER).orElse(null);
        if (masterServer == null) {
            throw new ServiceException("no master server available");
        }
        final WorkflowInstanceRecoverSuspendTasksRequest recoverSuspendTaskRequest =
                WorkflowInstanceRecoverSuspendTasksRequest.builder()
                        .workflowInstanceId(workflowInstance.getId())
                        .userId(workflowInstanceControlRequest.executeUser.getId())
                        .build();

        final WorkflowInstanceRecoverSuspendTasksResponse recoverSuspendTaskResponse = Clients
                .withService(IWorkflowControlClient.class)
                .withHost(masterServer.getHost() + ":" + masterServer.getPort())
                .triggerFromSuspendTasks(recoverSuspendTaskRequest);
        if (!recoverSuspendTaskResponse.isSuccess()) {

View on GitHub (pinned to 02eac45a1b)

Solutions

  1. Check the instance state before recovering: only paused/stopped instances qualify
  2. Refresh instance status — it may already have been resumed
  3. Use recover-from-failed-tasks for failure states and kill-recovery for killed states instead
  4. Catch this validation error in automation and treat it as a no-op when the instance already resumed

Example fix

// before
api.recoverSuspendedWorkflowInstance(instanceId, userId);
// after
WorkflowInstance wf = api.queryWorkflowInstance(instanceId);
if (wf.getState().isPaused() || wf.getState().isStopped()) {
    api.recoverSuspendedWorkflowInstance(instanceId, userId);
}
Defensive patterns

Strategy: validation

Validate before calling

WorkflowExecutionStatus st = workflowInstance.getState();
if (!st.isPaused() && !st.isStopped()) {
    throw new IllegalStateException("State " + st + " is not recoverable from suspension");
}

Type guard

boolean recoverableFromSuspend(WorkflowInstance wf) { return wf.getState().isPaused() || wf.getState().isStopped(); }

Try / catch

try { delegate.recoverSuspended(op); } catch (ServiceException e) { if (e.getMessage().contains("cannot recovery")) { log.info("Instance not paused/stopped; skipping"); } else { throw e; } }

Prevention

When it happens

Trigger: RecoverSuspendedWorkflowInstanceExecutorDelegate.execute is called for an instance with state like running, success, failure, or killed — isPaused() and isStopped() both false.

Common situations: Recovering an already-recovered instance (now running); user clicks 'recover suspended' on a killed instance instead of using the correct recovery path; stale UI state after someone else resumed the workflow; scripts using the wrong recovery endpoint for failed tasks.

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/5cc1588d73e32a4d. Report an issue: GitHub.