n8n-io/n8n · error · PlanValidationError
Checkpoint task "${task.id}" must depend on at least one bui
Error message
Checkpoint task "${task.id}" must depend on at least one build-workflow task What it means
Thrown as a PlanValidationError by validateDependencies (planned-task-service.ts:44-48) when a task with kind === 'checkpoint' has an empty deps array. Checkpoints represent user-approval gates and must gate at least one build-workflow task; a checkpoint with no deps has nothing to gate. This is the first of two checkpoint-dep checks (the second, error 515, requires at least one dep to be build-workflow kind).
Source
Thrown at packages/@n8n/instance-ai/src/planned-tasks/planned-task-service.ts:45
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`,
);
}
}
}
const visiting = new Set<string>();
const visited = new Set<string>();
const visit = (taskId: string) => {View on GitHub (pinned to 5ac6606e81)
Solutions
- Add at least one dep to the checkpoint, pointing at the build-workflow task(s) it should gate.
- Reconsider whether a checkpoint is needed at all — if there is nothing to gate, remove it.
- Re-call the plan tool with the corrected checkpoint deps.
Example fix
// before: { id: 'cp1', kind: 'checkpoint', deps: [] }
// after: { id: 'cp1', kind: 'checkpoint', deps: ['build-1'] } Defensive patterns
Strategy: validation
Validate before calling
for (const t of tasks) {
if (t.kind === 'checkpoint' && t.deps.length === 0) {
throw new Error(`Checkpoint "${t.id}" needs at least one dep`);
}
} Type guard
function checkpointsHaveDeps(tasks: { kind: string; deps: string[] }[]): boolean {
return tasks.every(t => t.kind !== 'checkpoint' || t.deps.length > 0);
} Try / catch
try { await coordinator.createPlan(threadId, tasks, meta); }
catch (e) {
if (e instanceof PlanValidationError) {
// surface to LLM for retry
}
throw e;
} Prevention
- Always wire checkpoint deps to the build-workflow tasks they gate.
- If nothing is gated, drop the checkpoint.
- Validate checkpoint shape locally before submission.
When it happens
Trigger: A submitted plan includes { kind: 'checkpoint', deps: [] }. The empty-deps check runs before the kind check, so this fires even if non-build-workflow deps would otherwise be acceptable.
Common situations: LLM models a checkpoint as a standalone task; a programmatic builder creates a checkpoint template and forgets to wire deps; the plan was edited and deps were cleared.
Related errors
- Plan contains duplicate task IDs
- Task "${task.id}" depends on unknown task "${depId}"
- Plan contains a dependency cycle involving "${taskId}"
- Cannot decrease maxIterations when resuming a run. Expected
- Checkpoint for runId ${this.runId} has pending tool calls —
AI-assisted analysis of n8n-io/n8n@5ac6606e81 (2026-08-12).
Data as JSON: /api/errors/c0898e424603a52b.
Report an issue: GitHub.