eyaltoledano/claude-task-master · error · Error

Invalid subtask ID: ${subId}. Subtask ID must be a positive

Error message

Invalid subtask ID: ${subId}. Subtask ID must be a positive integer.

What it means

After splitting a dotted subtask ID, updateSubtaskStatusInFile() validates that the subtask segment is a positive integer (digits only). If the portion after the dot is non-numeric (e.g. '5.a' or '5.2x'), it throws this error so downstream numeric comparisons against subtask ids are safe.

Source

Thrown at packages/tm-core/src/modules/storage/adapters/file-storage/file-storage.ts:516

	 */
	private async updateSubtaskStatusInFile(
		tasks: Task[],
		subtaskId: string,
		newStatus: TaskStatus,
		tag?: string
	): Promise<UpdateStatusResult> {
		// Parse the subtask ID to get parent ID and subtask ID
		const parts = subtaskId.split('.');
		if (parts.length !== 2) {
			throw new Error(
				`Invalid subtask ID format: ${subtaskId}. Expected format: parentId.subtaskId`
			);
		}

		const [parentId, subIdRaw] = parts;
		const subId = subIdRaw.trim();
		if (!/^\d+$/.test(subId)) {
			throw new Error(
				`Invalid subtask ID: ${subId}. Subtask ID must be a positive integer.`
			);
		}
		const subtaskNumericId = Number(subId);

		// Find the parent task
		const parentTaskIndex = tasks.findIndex(
			(t) => String(t.id) === String(parentId)
		);

		if (parentTaskIndex === -1) {
			throw new Error(`Parent task ${parentId} not found`);
		}

		const parentTask = tasks[parentTaskIndex];

		// Find the subtask within the parent task
		const subtaskIndex = parentTask.subtasks.findIndex(

View on GitHub (pinned to c0c98d367c)

Solutions

  1. Sanitize the subtask segment to digits-only before calling (e.g. parseInt and re-validate)
  2. Use /^\d+\.\d+$/ to validate the full dotted ID up front
  3. Confirm the subtask's numeric index from the parent task's subtasks list
  4. Reject/reprompt on invalid user input before invoking the storage API

Example fix

// before
await storage.updateTaskStatus(`5.${userInput}`, 'done'); // 'abc' throws
// after
const subId = Number.parseInt(userInput, 10);
if (!Number.isInteger(subId) || subId <= 0) {
  throw new Error(`Subtask index must be a positive integer, got: ${userInput}`);
}
await storage.updateTaskStatus(`5.${subId}`, 'done');
Defensive patterns

Strategy: validation

Validate before calling

function isPositiveIntSegment(seg: string): boolean {
  return /^\d+$/.test(seg.trim());
}
function canUpdateSubtask(id: string): boolean {
  const [parent, sub] = id.split('.');
  return id.split('.').length === 2 && /^\d+$/.test(parent) && isPositiveIntSegment(sub);
}
// guard: if (!canUpdateSubtask(id)) reject();

Type guard

function isInvalidSubtaskIdError(e: unknown): e is Error {
  return e instanceof Error && e.message.startsWith('Invalid subtask ID:');
}

Try / catch

try {
  await storage.updateTaskStatus(subtaskId, newStatus);
} catch (e) {
  if (isInvalidSubtaskIdError(e)) {
    // coerce/re-prompt: const n = parseInt(sub, 10); if (!Number.isInteger(n) || n <= 0) ...
  } else throw e;
}

Prevention

When it happens

Trigger: Passing an ID like '5.abc', '5.2x', or '5.-1' where the subtask segment is not purely digits; usually the result of unvalidated user input or mixing ID schemes from another system.

Common situations: CLI users typing descriptive text instead of the numeric subtask index, importing IDs from external trackers with alphanumeric keys, or string concatenation bugs building the subtask ID dynamically.

Related errors


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