eyaltoledano/claude-task-master · error

Generated subtasks must have unique ids

Error message

Generated subtasks must have unique ids

What it means

ensureSequentialSubtaskIds() also enforces that all generated subtask ids are distinct. It builds a Set of ids and compares size; duplicates mean the AI returned two subtasks sharing an id, which would collide in tasks.json. Thrown before any data is persisted, so the task file stays intact.

Source

Thrown at scripts/modules/task-manager/scope-adjustment.js:456

			preserved: preservedSubtasks.length,
			generated: 0,
			error: error.message
		};
	}
}

function ensureSequentialSubtaskIds(subtasks) {
	if (!Array.isArray(subtasks) || subtasks.length === 0) {
		return;
	}

	const ids = subtasks.map((subtask) => subtask.id);
	if (ids.some((id) => !Number.isInteger(id) || id < 1)) {
		throw new Error('Generated subtask ids must be positive integers');
	}
	const uniqueIds = new Set(ids);
	if (uniqueIds.size !== ids.length) {
		throw new Error('Generated subtasks must have unique ids');
	}

	const sortedIds = [...uniqueIds].sort((a, b) => a - b);
	for (let index = 0; index < sortedIds.length; index += 1) {
		if (sortedIds[index] !== index + 1) {
			throw new Error(
				'Generated subtask ids must be sequential starting from 1'
			);
		}
	}
}

/**
 * Generates AI prompt for scope adjustment
 * @param {Object} task - The task to adjust
 * @param {string} direction - 'up' or 'down'
 * @param {string} strength - 'light', 'regular', or 'heavy'
 * @param {string} customPrompt - Optional custom instructions

View on GitHub (pinned to c0c98d367c)

Solutions

  1. Retry the operation — regenerate the subtasks; duplicates are usually a one-off sampling artifact.
  2. Deduplicate before validation if you control the pipeline: const seen = new Set(); subtasks.filter(s => !seen.has(s.id) && seen.add(s.id)).
  3. Switch to a model that reliably produces schema-conformant JSON, or strengthen the prompt instruction to use strictly increasing ids.
  4. Lower the requested subtask count (reduce complexity threshold) to make valid output more likely.

Example fix

// before
[{ "id": 1, "title": "a" }, { "id": 1, "title": "b" }]
// after
[{ "id": 1, "title": "a" }, { "id": 2, "title": "b" }]
Defensive patterns

Strategy: validation

Validate before calling

function hasNoDuplicateIds(subtasks) {
  const ids = subtasks.map((s) => s.id);
  return new Set(ids).size === ids.length;
}
if (!hasNoDuplicateIds(generated.subtasks)) {
  const seen = new Set();
  generated.subtasks = generated.subtasks.filter((s) => !seen.has(s.id) && seen.add(s.id));
}

Type guard

function idsAreUnique(subtasks) {
  const ids = subtasks.map((s) => s.id);
  return ids.every((id, i) => ids.indexOf(id) === i) && ids.every((id) => Number.isInteger(id) && id >= 1);
}

Try / catch

try {
  await scopeDownTask([taskId], strength);
} catch (err) {
  if (err.message.includes('must have unique ids')) {
    console.error('Duplicate subtask IDs from AI generation — rerun the scope operation');
  } else throw err;
}

Prevention

When it happens

Trigger: scopeUpTask/scopeDownTask flows where the model returns duplicate ids such as [{id:1},{id:1},{id:2}], commonly when the model is asked for many subtasks and repeats an index, or when string vs number coercion masks duplicates ('1' vs 1 mapping to the same value after parsing).

Common situations: Large complexity-based expansions requesting 10+ subtasks from a weaker model, retries that merge old and new subtask lists without deduplication, or custom output parsing that doesn't dedupe.

Related errors


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