n8n-io/n8n · error · PlanValidationError

Task "${task.id}" depends on unknown task "${depId}"

Error message

Task "${task.id}" depends on unknown task "${depId}"

What it means

Thrown as a PlanValidationError by validateDependencies (planned-task-service.ts:38-41) when a task's deps array references an id that is not present in the tasks list. The knownIds set is built from submitted task ids, so any typo or dangling reference is caught. Like other PlanValidationError throws, plan.tool.ts catches it and returns the message as a tool result for the LLM to revise.

Source

Thrown at packages/@n8n/instance-ai/src/planned-tasks/planned-task-service.ts:40

		this.name = 'PlanValidationError';
	}
}

function hasDuplicateIds(tasks: PlannedTask[]): boolean {
	return new Set(tasks.map((task) => task.id)).size !== tasks.length;
}

function validateDependencies(tasks: PlannedTask[]): void {
	if (hasDuplicateIds(tasks)) {
		throw new PlanValidationError('Plan contains duplicate task IDs');
	}

	const knownIds = new Set(tasks.map((task) => task.id));
	const byId = new Map(tasks.map((task) => [task.id, task]));
	for (const task of tasks) {
		for (const depId of task.deps) {
			if (!knownIds.has(depId)) {
				throw new PlanValidationError(`Task "${task.id}" depends on unknown task "${depId}"`);
			}
		}
		if (task.kind === 'checkpoint') {
			if (task.deps.length === 0) {
				throw new PlanValidationError(
					`Checkpoint task "${task.id}" must depend on at least one build-workflow task`,
				);
			}
			const dependsOnBuildWorkflow = task.deps.some(
				(depId) => byId.get(depId)?.kind === 'build-workflow',
			);
			if (!dependsOnBuildWorkflow) {
				throw new PlanValidationError(
					`Checkpoint task "${task.id}" must depend on at least one build-workflow task`,
				);
			}
		}
	}

View on GitHub (pinned to 5ac6606e81)

Solutions

  1. Ensure every entry in each task.deps matches the id of another task in the same batch.
  2. Normalize ids (trim, consistent case) before submission.
  3. If a dep is no longer needed, remove it from deps rather than leaving a dangling reference.
  4. Re-call the tool with the corrected graph as the error message instructs.

Example fix

// before: [{ id: 'a', deps: [] }, { id: 'b', deps: ['c'] }]  // 'c' missing
// after:  [{ id: 'a', deps: [] }, { id: 'b', deps: ['a'] }]
Defensive patterns

Strategy: validation

Validate before calling

const ids = new Set(tasks.map(t => t.id));
for (const t of tasks) for (const dep of t.deps) {
  if (!ids.has(dep)) throw new Error(`Task "${t.id}" depends on unknown task "${dep}"`);
}

Type guard

function allDepsResolve(tasks: { id: string; deps: string[] }[]): boolean {
  const ids = new Set(tasks.map(t => t.id));
  return tasks.every(t => t.deps.every(d => ids.has(d)));
}

Try / catch

try { await coordinator.createPlan(threadId, tasks, meta); }
catch (e) {
  if (e instanceof PlanValidationError) {
    // return message to LLM; do not rethrow non-validation errors
  }
  throw e;
}

Prevention

When it happens

Trigger: A task lists a dep id that was never defined, was deleted, or is misspelled; ids differ only by case ('Task-1' vs 'task-1'); a dep references a task from a different plan batch.

Common situations: LLM renames a task but forgets to update dependents; manual plan editing drops a task without cleaning deps; id casing/whitespace mismatch.

Related errors


AI-assisted analysis of n8n-io/n8n@5ac6606e81 (2026-08-12). Data as JSON: /api/errors/d98d24cd169782f6. Report an issue: GitHub.