eyaltoledano/claude-task-master · error

Cannot create circular dependency: task ${parentIdNum} is al

Error message

Cannot create circular dependency: task ${parentIdNum} is already a subtask or dependent of task ${existingTaskIdNum}

What it means

addSubtask uses isTaskDependentOn to detect whether attaching the existing task under the parent would create a cycle: if the parent is already a subtask of, or depends on, the task being converted, the hierarchy would become circular. The error names both IDs to help untangle the dependency graph.

Source

Thrown at scripts/modules/task-manager/add-subtask.js:79

			const existingTask = data.tasks[existingTaskIndex];

			// Check if task is already a subtask
			if (existingTask.parentTaskId) {
				throw new Error(
					`Task ${existingTaskIdNum} is already a subtask of task ${existingTask.parentTaskId}`
				);
			}

			// Check for circular dependency
			if (existingTaskIdNum === parentIdNum) {
				throw new Error(`Cannot make a task a subtask of itself`);
			}

			// Check if parent task is a subtask of the task we're converting
			// This would create a circular dependency
			if (isTaskDependentOn(data.tasks, parentTask, existingTaskIdNum)) {
				throw new Error(
					`Cannot create circular dependency: task ${parentIdNum} is already a subtask or dependent of task ${existingTaskIdNum}`
				);
			}

			// Find the highest subtask ID to determine the next ID
			const highestSubtaskId =
				parentTask.subtasks.length > 0
					? Math.max(...parentTask.subtasks.map((st) => st.id))
					: 0;
			const newSubtaskId = highestSubtaskId + 1;

			// Clone the existing task to be converted to a subtask
			newSubtask = {
				...existingTask,
				id: newSubtaskId,
				parentTaskId: parentIdNum
			};

View on GitHub (pinned to c0c98d367c)

Solutions

  1. Inspect the dependency graph of both tasks (task-master shows dependencies) to find the cycle.
  2. Remove the offending dependency or subtask relationship first, then retry the conversion.
  3. Choose a different parent task that does not depend on the candidate child.
  4. Restructure so the hierarchy is acyclic (e.g. convert the parent instead).

Example fix

// before
await addSubtask('2', '1'); // task 2 depends on task 1 → cycle
// after
// remove task 2's dependency on task 1 first, then:
await addSubtask('2', '1');
Defensive patterns

Strategy: validation

Validate before calling

const data = readJSON(tasksPath, projectRoot, tag);
const parent = data.tasks.find(t => t.id === parseInt(parentId, 10));
const child = data.tasks.find(t => t.id === parseInt(existingTaskId, 10));
if (parent && child && isTaskDependentOn(data.tasks, parent, child.id)) {
  throw new Error(`Cycle: task ${parent.id} already depends on task ${child.id}`);
}

Type guard

function createsCycle(parent, childId, deps = parent?.dependencies || []) {
  return deps.includes(childId) || parent?.parentTaskId === childId;
}

Try / catch

try {
  await addSubtask(tasksPath, parentId, existingTaskId);
} catch (err) {
  if (err.message.startsWith('Cannot create circular dependency')) {
    // remove the offending dependency or pick a different parent
  } else throw err;
}

Prevention

When it happens

Trigger: Calling addSubtask(parentId, existingTaskId) where parentId is a subtask/dependent of existingTaskId — e.g. converting task A into a subtask of task B when B already depends on A.

Common situations: Complex dependency graphs built up over time, scripting batch conversions without cycle analysis, or users reorganizing tasks without visualizing dependencies.

Related errors


AI-assisted analysis of eyaltoledano/claude-task-master@c0c98d367c (2026-08-29). Data as JSON: /api/errors/c601d7c99be59fc3. Report an issue: GitHub.