{"record":{"id":"b990aa350d66b5d4","repo":"apache/dolphinscheduler","slug":"50019","errorCode":"50019","errorMessage":"workflow node has cycle","messagePattern":"workflow node has cycle","errorType":"exception","errorClass":"ServiceException","httpStatus":null,"severity":"error","filePath":"dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/WorkflowDefinitionServiceImpl.java","lineNumber":420,"sourceCode":"                    .map(workflowTaskRelationLog -> JSONUtils.parseObject(\n                            JSONUtils.toJsonString(workflowTaskRelationLog),\n                            WorkflowTaskRelation.class))\n                    .collect(Collectors.toList());\n            List<TaskNode> taskNodeList = processService.transformTask(workflowTaskRelations, taskDefinitionLogs);\n            if (taskNodeList.size() != taskRelationList.size()) {\n                Set<Long> postTaskCodes = taskRelationList.stream().map(WorkflowTaskRelationLog::getPostTaskCode)\n                        .collect(Collectors.toSet());\n                Set<Long> taskNodeCodes = taskNodeList.stream().map(TaskNode::getCode).collect(Collectors.toSet());\n                Collection<Long> codes = CollectionUtils.subtract(postTaskCodes, taskNodeCodes);\n                if (CollectionUtils.isNotEmpty(codes)) {\n                    String taskCodes = StringUtils.join(codes, Constants.COMMA);\n                    log.error(\"Task definitions do not exist, taskCodes:{}.\", taskCodes);\n                    throw new ServiceException(Status.TASK_DEFINE_NOT_EXIST, taskCodes);\n                }\n            }\n            if (graphHasCycle(taskNodeList)) {\n                log.error(\"workflow DAG has cycle.\");\n                throw new ServiceException(Status.WORKFLOW_NODE_HAS_CYCLE);\n            }\n\n            // check whether the task relation json is normal\n            for (WorkflowTaskRelationLog workflowTaskRelationLog : taskRelationList) {\n                if (workflowTaskRelationLog.getPostTaskCode() == 0) {\n                    log.error(\"The post_task_code or post_task_version of workflowTaskRelationLog can not be zero, \" +\n                            \"workflowTaskRelationLogId:{}.\", workflowTaskRelationLog.getId());\n                    throw new ServiceException(Status.CHECK_WORKFLOW_TASK_RELATION_ERROR);\n                }\n            }\n            return taskRelationList;\n        } catch (ServiceException ex) {\n            throw ex;\n        } catch (Exception e) {\n            log.error(\"Check task relation list error, meet an unknown exception, given taskRelationJson: {}\",\n                    taskRelationJson, e);\n            throw new ServiceException(Status.REQUEST_PARAMS_NOT_VALID_ERROR);\n        }","sourceCodeStart":402,"sourceCodeEnd":438,"githubUrl":"https://github.com/apache/dolphinscheduler/blob/02eac45a1b6676e639fcbfb4be2243de5771b05d/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/WorkflowDefinitionServiceImpl.java#L402-L438","documentation":"Thrown when the workflow DAG built from the task relations contains a cycle. DolphinScheduler workflows must be a directed acyclic graph; graphHasCycle detects dependency loops among the task nodes before persisting.","triggerScenarios":"updateWorkflowDefinition/importWorkflowDefinition where relations form a loop, e.g. task A's postTaskCode is B and B's postTaskCode is A, or a task depends on itself.","commonSituations":"Manually editing relation JSON and introducing a back edge; importing a workflow exported from another tool allowing cycles; merge conflicts in workflow JSON leaving both directions of a dependency.","solutions":["Inspect the relation list and remove the edge(s) that close the loop","Redraw dependencies in the UI designer, which prevents cyclic connections","Topologically validate your relation JSON (e.g. with a Kahn's-algorithm script) before submitting"],"exampleFix":"// before\n[{pre:A,post:B},{pre:B,post:A}] // cycle\n// after\n[{pre:A,post:B}]","handlingStrategy":"validation","validationCode":"// Kahn's algorithm cycle check before submitting\nMap<Long, List<Long>> adj = buildAdjacency(relations); Deque<Long> q = nodesWithZeroIndegree(adj);\nint visited = 0; while (!q.isEmpty()) { Long n = q.poll(); visited++; for (Long m : adj.get(n)) if (--indeg(m) == 0) q.add(m); }\nif (visited != nodeCount) throw new IllegalArgumentException(\"relation graph has a cycle\");","typeGuard":null,"tryCatchPattern":"try { ... } catch (ServiceException e) { if (e.getCode() == 50019) { /* redraw DAG without back edges */ } }","preventionTips":["Design DAGs in the UI, which forbids cyclic edges","Run a local topological sort on generated relation JSON","Review hand-edited relations for back edges"],"tags":["api","dag","cycle","dolphinscheduler"],"backgroundTag":"invalid-argument-value","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"}