apache/dolphinscheduler · error · ServiceException
50019
50019
Error message
workflow node has cycle
What it means
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.
Source
Thrown at dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/WorkflowDefinitionServiceImpl.java:420
.map(workflowTaskRelationLog -> JSONUtils.parseObject(
JSONUtils.toJsonString(workflowTaskRelationLog),
WorkflowTaskRelation.class))
.collect(Collectors.toList());
List<TaskNode> taskNodeList = processService.transformTask(workflowTaskRelations, taskDefinitionLogs);
if (taskNodeList.size() != taskRelationList.size()) {
Set<Long> postTaskCodes = taskRelationList.stream().map(WorkflowTaskRelationLog::getPostTaskCode)
.collect(Collectors.toSet());
Set<Long> taskNodeCodes = taskNodeList.stream().map(TaskNode::getCode).collect(Collectors.toSet());
Collection<Long> codes = CollectionUtils.subtract(postTaskCodes, taskNodeCodes);
if (CollectionUtils.isNotEmpty(codes)) {
String taskCodes = StringUtils.join(codes, Constants.COMMA);
log.error("Task definitions do not exist, taskCodes:{}.", taskCodes);
throw new ServiceException(Status.TASK_DEFINE_NOT_EXIST, taskCodes);
}
}
if (graphHasCycle(taskNodeList)) {
log.error("workflow DAG has cycle.");
throw new ServiceException(Status.WORKFLOW_NODE_HAS_CYCLE);
}
// check whether the task relation json is normal
for (WorkflowTaskRelationLog workflowTaskRelationLog : taskRelationList) {
if (workflowTaskRelationLog.getPostTaskCode() == 0) {
log.error("The post_task_code or post_task_version of workflowTaskRelationLog can not be zero, " +
"workflowTaskRelationLogId:{}.", workflowTaskRelationLog.getId());
throw new ServiceException(Status.CHECK_WORKFLOW_TASK_RELATION_ERROR);
}
}
return taskRelationList;
} catch (ServiceException ex) {
throw ex;
} catch (Exception e) {
log.error("Check task relation list error, meet an unknown exception, given taskRelationJson: {}",
taskRelationJson, e);
throw new ServiceException(Status.REQUEST_PARAMS_NOT_VALID_ERROR);
}View on GitHub (pinned to 02eac45a1b)
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
Example fix
// before
[{pre:A,post:B},{pre:B,post:A}] // cycle
// after
[{pre:A,post:B}] Defensive patterns
Strategy: validation
Validate before calling
// Kahn's algorithm cycle check before submitting
Map<Long, List<Long>> adj = buildAdjacency(relations); Deque<Long> q = nodesWithZeroIndegree(adj);
int visited = 0; while (!q.isEmpty()) { Long n = q.poll(); visited++; for (Long m : adj.get(n)) if (--indeg(m) == 0) q.add(m); }
if (visited != nodeCount) throw new IllegalArgumentException("relation graph has a cycle"); Try / catch
try { ... } catch (ServiceException e) { if (e.getCode() == 50019) { /* redraw DAG without back edges */ } } Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
AI-assisted analysis of apache/dolphinscheduler@02eac45a1b (2026-09-06).
Data as JSON: /api/errors/b990aa350d66b5d4.
Report an issue: GitHub.