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
- Sanitize the subtask segment to digits-only before calling (e.g. parseInt and re-validate)
- Use /^\d+\.\d+$/ to validate the full dotted ID up front
- Confirm the subtask's numeric index from the parent task's subtasks list
- 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
- Coerce the subtask segment with parseInt and reject NaN/<=0 before calling
- Reject alphanumeric external tracker keys at the input boundary
- Validate the whole dotted ID with /^\d+\.\d+$/ before any storage call
- When building IDs via concatenation, assert numeric segments first
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
- Invalid subtask ID format: ${subtaskId}. Expected format: pa
- Invalid iterations: ${iterations}. Must be a positive intege
- Invalid format: ${options.format}. Valid formats are: text,
- PARENT_TASK_NOT_FOUND
- PARENT_TASK_NO_SUBTASKS
AI-assisted analysis of eyaltoledano/claude-task-master@c0c98d367c (2026-08-29).
Data as JSON: /api/errors/3b24ff03b63aacfa.
Report an issue: GitHub.