{"record":{"id":"853f8617a5502a50","repo":"eyaltoledano/claude-task-master","slug":"invalid-subtask-id-format-subtaskid-both-pare","errorCode":null,"errorMessage":"Invalid subtask ID format: ${subtaskId}. Both parent ID and subtask ID must be positive integers.","messagePattern":"Invalid subtask ID format: (.+?)\\. Both parent ID and subtask ID must be positive integers\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"scripts/modules/task-manager/update-subtask-by-id.js","lineNumber":150,"sourceCode":"\n\t\tconst data = readJSON(tasksPath, projectRoot, tag);\n\t\tif (!data || !data.tasks) {\n\t\t\tthrow new Error(\n\t\t\t\t`No valid tasks found in ${tasksPath}. The file may be corrupted or have an invalid format.`\n\t\t\t);\n\t\t}\n\n\t\tconst [parentIdStr, subtaskIdStr] = subtaskId.split('.');\n\t\tconst parentId = parseInt(parentIdStr, 10);\n\t\tconst subtaskIdNum = parseInt(subtaskIdStr, 10);\n\n\t\tif (\n\t\t\tNumber.isNaN(parentId) ||\n\t\t\tparentId <= 0 ||\n\t\t\tNumber.isNaN(subtaskIdNum) ||\n\t\t\tsubtaskIdNum <= 0\n\t\t) {\n\t\t\tthrow new Error(\n\t\t\t\t`Invalid subtask ID format: ${subtaskId}. Both parent ID and subtask ID must be positive integers.`\n\t\t\t);\n\t\t}\n\n\t\tconst parentTask = data.tasks.find((task) => task.id === parentId);\n\t\tif (!parentTask) {\n\t\t\tthrow new Error(\n\t\t\t\t`Parent task with ID ${parentId} not found. Please verify the task ID and try again.`\n\t\t\t);\n\t\t}\n\n\t\tif (!parentTask.subtasks || !Array.isArray(parentTask.subtasks)) {\n\t\t\tthrow new Error(`Parent task ${parentId} has no subtasks.`);\n\t\t}\n\n\t\tconst subtaskIndex = parentTask.subtasks.findIndex(\n\t\t\t(st) => st.id === subtaskIdNum\n\t\t);","sourceCodeStart":132,"sourceCodeEnd":168,"githubUrl":"https://github.com/eyaltoledano/claude-task-master/blob/c0c98d367c55296bfe69e65680625b6db437af02/scripts/modules/task-manager/update-subtask-by-id.js#L132-L168","documentation":"After splitting the dotted ID, both halves are parsed as integers; if either is NaN or <= 0 the format is rejected. This catches IDs like '5.0', 'a.2', '5.-1', or '5.2.3' leftovers that fail parseInt.","triggerScenarios":"Passing '0.2' or '5.0', alphabetic segments ('HAM.1' in file mode), or malformed strings with extra segments.","commonSituations":"Mixing API-style IDs with file-mode calls, off-by-one using subtask index 0, template variables left unexpanded ('${parentId}.2').","solutions":["Ensure both segments are positive integers, e.g. '5.2'","Validate with a regex like /^\\d+\\.\\d+$/ before calling","Use API-style IDs only via the remote bridge/update-task path"],"exampleFix":"// before\nawait updateSubtaskById('5.0', prompt, options); // 0 is invalid\n// after\nif (!/^\\d+\\.\\d+$/.test(subtaskId)) throw new Error(`Bad subtask ID: ${subtaskId}`);\nawait updateSubtaskById('5.1', prompt, options);","handlingStrategy":"validation","validationCode":"const m = /^\\d+\\.\\d+$/.exec(subtaskId);\nif (!m || Number(subtaskId.split('.')[0]) <= 0 || Number(subtaskId.split('.')[1]) <= 0) {\n  throw new Error(`Subtask ID must be parentId.subtaskId with positive integers: ${subtaskId}`);\n}","typeGuard":"function isPositiveDottedId(v) {\n  if (typeof v !== 'string') return false;\n  const [a, b] = v.split('.');\n  return Number.isInteger(+a) && +a > 0 && Number.isInteger(+b) && +b > 0;\n}","tryCatchPattern":"try {\n  await updateSubtaskById(subtaskId, prompt, options);\n} catch (err) {\n  if (err.message.includes('must be positive integers')) {\n    console.error(`Fix ID segments (no 0/negatives/letters): ${subtaskId}`);\n  } else throw err;\n}","preventionTips":["Regex-validate IDs before calling","Avoid 0-based subtask indices","Expand template variables before building ID strings"],"tags":["validation","id-format"],"backgroundTag":"invalid-id-format","analyzedSha":"c0c98d367c55296bfe69e65680625b6db437af02","analyzedAt":"2026-08-29T02:56:26.071Z","schemaVersion":2},"datasetVersion":"2026-08-29T07:17:48.351Z"}