{"record":{"id":"5cc1588d73e32a4d","repo":"apache/dolphinscheduler","slug":"the-workflow-instance-s-state-is-s-cannot-reco","errorCode":null,"errorMessage":"The workflow instance: %s state is %s, cannot recovery","messagePattern":"The workflow instance: (.+?) state is (.+?), cannot recovery","errorType":"exception","errorClass":"ServiceException","httpStatus":null,"severity":"warning","filePath":"dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/executor/workflow/RecoverSuspendedWorkflowInstanceExecutorDelegate.java","lineNumber":46,"sourceCode":"import org.apache.dolphinscheduler.registry.api.RegistryClient;\nimport org.apache.dolphinscheduler.registry.api.enums.RegistryNodeType;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class RecoverSuspendedWorkflowInstanceExecutorDelegate\n        implements\n            IExecutorDelegate<RecoverSuspendedWorkflowInstanceExecutorDelegate.RecoverSuspendedWorkflowInstanceOperation, Void> {\n\n    @Autowired\n    private RegistryClient registryClient;\n\n    @Override\n    public Void execute(RecoverSuspendedWorkflowInstanceOperation workflowInstanceControlRequest) {\n        final WorkflowInstance workflowInstance = workflowInstanceControlRequest.workflowInstance;\n        if (!workflowInstance.getState().isPaused() && !workflowInstance.getState().isStopped()) {\n            throw new ServiceException(\n                    String.format(\"The workflow instance: %s state is %s, cannot recovery\", workflowInstance.getName(),\n                            workflowInstance.getState()));\n        }\n        final Server masterServer = registryClient.getRandomServer(RegistryNodeType.MASTER).orElse(null);\n        if (masterServer == null) {\n            throw new ServiceException(\"no master server available\");\n        }\n        final WorkflowInstanceRecoverSuspendTasksRequest recoverSuspendTaskRequest =\n                WorkflowInstanceRecoverSuspendTasksRequest.builder()\n                        .workflowInstanceId(workflowInstance.getId())\n                        .userId(workflowInstanceControlRequest.executeUser.getId())\n                        .build();\n\n        final WorkflowInstanceRecoverSuspendTasksResponse recoverSuspendTaskResponse = Clients\n                .withService(IWorkflowControlClient.class)\n                .withHost(masterServer.getHost() + \":\" + masterServer.getPort())\n                .triggerFromSuspendTasks(recoverSuspendTaskRequest);\n        if (!recoverSuspendTaskResponse.isSuccess()) {","sourceCodeStart":28,"sourceCodeEnd":64,"githubUrl":"https://github.com/apache/dolphinscheduler/blob/02eac45a1b6676e639fcbfb4be2243de5771b05d/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/executor/workflow/RecoverSuspendedWorkflowInstanceExecutorDelegate.java#L28-L64","documentation":"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.","triggerScenarios":"RecoverSuspendedWorkflowInstanceExecutorDelegate.execute is called for an instance with state like running, success, failure, or killed — isPaused() and isStopped() both false.","commonSituations":"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.","solutions":["Check the instance state before recovering: only paused/stopped instances qualify","Refresh instance status — it may already have been resumed","Use recover-from-failed-tasks for failure states and kill-recovery for killed states instead","Catch this validation error in automation and treat it as a no-op when the instance already resumed"],"exampleFix":"// before\napi.recoverSuspendedWorkflowInstance(instanceId, userId);\n// after\nWorkflowInstance wf = api.queryWorkflowInstance(instanceId);\nif (wf.getState().isPaused() || wf.getState().isStopped()) {\n    api.recoverSuspendedWorkflowInstance(instanceId, userId);\n}","handlingStrategy":"validation","validationCode":"WorkflowExecutionStatus st = workflowInstance.getState();\nif (!st.isPaused() && !st.isStopped()) {\n    throw new IllegalStateException(\"State \" + st + \" is not recoverable from suspension\");\n}","typeGuard":"boolean recoverableFromSuspend(WorkflowInstance wf) { return wf.getState().isPaused() || wf.getState().isStopped(); }","tryCatchPattern":"try { delegate.recoverSuspended(op); } catch (ServiceException e) { if (e.getMessage().contains(\"cannot recovery\")) { log.info(\"Instance not paused/stopped; skipping\"); } else { throw e; } }","preventionTips":["Only offer 'recover suspended' for paused/stopped instances in the UI","Refresh instance state before recovery actions","Use the matching recovery API per state (failure vs suspended vs killed)","In automation, re-query state after any pause/stop before recovery"],"tags":["dolphinscheduler","workflow-state","invalid-state-transition","recovery"],"backgroundTag":"invalid-state-transition","analyzedSha":"02eac45a1b6676e639fcbfb4be2243de5771b05d","analyzedAt":"2026-09-06T17:43:00.555Z","contentChangedAt":"2026-09-06T17:43:00.555Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}