{"record":{"id":"e9dbd94bce85052a","repo":"apache/dolphinscheduler","slug":"updateworkflowinstance-workflowinstanceid-stat-e9dbd9","errorCode":null,"errorMessage":"updateWorkflowInstance + workflowInstanceId + state failed, expect original state is + originalStatus.name() + actual state is : {} + workflowInstance.getState().name()","messagePattern":"updateWorkflowInstance \\+ workflowInstanceId \\+ state failed, expect original state is \\+ originalStatus\\.name\\(\\) \\+ actual state is : (.+?) \\+ workflowInstance\\.getState\\(\\)\\.name\\(\\)","errorType":"exception","errorClass":"UnsupportedOperationException","httpStatus":null,"severity":"error","filePath":"dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/repository/impl/WorkflowInstanceDaoImpl.java","lineNumber":76,"sourceCode":"    public void upsertWorkflowInstance(@NonNull WorkflowInstance workflowInstance) {\n        if (workflowInstance.getId() != null) {\n            updateById(workflowInstance);\n        } else {\n            insert(workflowInstance);\n        }\n    }\n\n    @Override\n    public void updateWorkflowInstanceState(Integer workflowInstanceId, WorkflowExecutionStatus originalStatus,\n                                            WorkflowExecutionStatus targetStatus) {\n        int update = mybatisMapper.updateWorkflowInstanceState(workflowInstanceId, originalStatus, targetStatus);\n        if (update != 1) {\n            WorkflowInstance workflowInstance = mybatisMapper.selectById(workflowInstanceId);\n            if (workflowInstance == null) {\n                throw new UnsupportedOperationException(\"updateWorkflowInstance \" + workflowInstanceId\n                        + \" state failed, the workflow instance is not exist in db\");\n            }\n            throw new UnsupportedOperationException(\n                    \"updateWorkflowInstance \" + workflowInstanceId + \" state failed, expect original state is \"\n                            + originalStatus.name() + \" actual state is : {} \" + workflowInstance.getState().name());\n        }\n    }\n\n    @Override\n    public void forceUpdateWorkflowInstanceState(Integer id, WorkflowExecutionStatus status) {\n        mybatisMapper.forceUpdateWorkflowInstanceState(id, status);\n    }\n\n    /**\n     * find last scheduler process instance in the date interval\n     *\n     * @param workflowDefinitionCode definitionCode\n     * @param taskDefinitionCode    definitionCode\n     * @param dateInterval          dateInterval\n     * @return process instance\n     */","sourceCodeStart":58,"sourceCodeEnd":94,"githubUrl":"https://github.com/apache/dolphinscheduler/blob/02eac45a1b6676e639fcbfb4be2243de5771b05d/dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/repository/impl/WorkflowInstanceDaoImpl.java#L58-L94","documentation":"updateWorkflowInstanceState uses optimistic-style state transition: the UPDATE only succeeds when the row's current state equals the expected originalStatus. If update count != 1 but the instance exists, its stored state differs from what the caller expected, and UnsupportedOperationException is thrown with both expected and actual states.","triggerScenarios":"Two threads/processes (e.g. master failover plus a kill command, or a retry) change the instance state concurrently so the expected originalStatus no longer matches; calling with a wrong originalStatus for the instance's current phase.","commonSituations":"Master/worker race conditions during failover; submitting state transitions twice (double kill/restart); code assuming READY_PAUSE/RUNNING when the instance already moved to STOP/FAILURE.","solutions":["Re-read the instance's current state and recompute the transition from the actual state.","Serialize state changes (single actor per instance) or tolerate already-transitioned states instead of throwing.","Retry the whole read-modify-write cycle when a concurrent transition is detected.","Log expected vs actual state and treat it as a benign conflict where the business logic allows."],"exampleFix":"// before\nworkflowInstanceDao.updateWorkflowInstanceState(id, WorkflowExecutionStatus.RUNNING, WorkflowExecutionStatus.STOP);\n// after\nWorkflowInstance wi = workflowInstanceDao.queryById(id);\nif (wi != null && wi.getState() == WorkflowExecutionStatus.RUNNING) {\n    workflowInstanceDao.updateWorkflowInstanceState(id, wi.getState(), WorkflowExecutionStatus.STOP);\n} else {\n    log.warn(\"instance {} already in state {}, skip stop\", id, wi == null ? null : wi.getState());\n}","handlingStrategy":"retry","validationCode":"WorkflowInstance wi = workflowInstanceDao.queryById(id);\nif (wi == null || wi.getState() != expectedOriginal) {\n    log.warn(\"instance {} state is {}, not {}; skip transition\", id, wi == null ? null : wi.getState(), expectedOriginal);\n    return;\n}","typeGuard":null,"tryCatchPattern":"try {\n    workflowInstanceDao.updateWorkflowInstanceState(id, original, target);\n} catch (UnsupportedOperationException e) {\n    // re-read state and retry the transition from actual state\n    WorkflowInstance wi = workflowInstanceDao.queryById(id);\n    if (wi != null) {\n        workflowInstanceDao.updateWorkflowInstanceState(id, wi.getState(), target);\n    }\n}","preventionTips":["Always base transitions on a freshly read state, not cached status.","Avoid concurrent actors issuing transitions for the same instance.","Make kill/restart idempotent: tolerate the instance already being in the target state.","Log expected vs actual state to diagnose race conditions."],"tags":["state-transition","concurrency","workflow-instance"],"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-14T00:17:10.932Z"}