{"record":{"id":"ca3fb868aecf25ea","repo":"eyaltoledano/claude-task-master","slug":"task-already-exists","errorCode":"TASK_ALREADY_EXISTS","errorMessage":"Cannot move to existing task ID ${destTaskId}. Choose a different ID or use subtask destination.","messagePattern":"Cannot move to existing task ID (.+?)\\. Choose a different ID or use subtask destination\\.","errorType":"exception","errorClass":"MoveTaskError","httpStatus":null,"severity":"error","filePath":"scripts/modules/task-manager/move-task.js","lineNumber":339,"sourceCode":"\t}\n\n\t// Find source subtask\n\tconst sourceSubtaskIndex = sourceParentTask.subtasks.findIndex(\n\t\t(st) => st.id === sourceSubtaskId\n\t);\n\tif (sourceSubtaskIndex === -1) {\n\t\tthrow new MoveTaskError(\n\t\t\tMOVE_ERROR_CODES.SUBTASK_NOT_FOUND,\n\t\t\t`Source subtask ${sourceId} not found`\n\t\t);\n\t}\n\n\tconst sourceSubtask = sourceParentTask.subtasks[sourceSubtaskIndex];\n\n\t// Check if destination task exists\n\tconst existingDestTask = tasks.find((t) => t.id === destTaskId);\n\tif (existingDestTask) {\n\t\tthrow new MoveTaskError(\n\t\t\tMOVE_ERROR_CODES.TASK_ALREADY_EXISTS,\n\t\t\t`Cannot move to existing task ID ${destTaskId}. Choose a different ID or use subtask destination.`\n\t\t);\n\t}\n\n\t// Create new task from subtask\n\tconst newTask = {\n\t\tid: destTaskId,\n\t\ttitle: sourceSubtask.title,\n\t\tdescription: sourceSubtask.description,\n\t\tstatus: sourceSubtask.status || 'pending',\n\t\tdependencies: sourceSubtask.dependencies || [],\n\t\tpriority: sourceSubtask.priority || 'medium',\n\t\tdetails: sourceSubtask.details || '',\n\t\ttestStrategy: sourceSubtask.testStrategy || '',\n\t\tsubtasks: []\n\t};\n","sourceCodeStart":321,"sourceCodeEnd":357,"githubUrl":"https://github.com/eyaltoledano/claude-task-master/blob/c0c98d367c55296bfe69e65680625b6db437af02/scripts/modules/task-manager/move-task.js#L321-L357","documentation":"This MoveTaskError with code TASK_ALREADY_EXISTS is thrown when moving a subtask out to become a standalone task, but the destination task ID is already occupied in the task list. The library refuses to overwrite or swap tasks implicitly during a subtask-to-task move, because doing so would silently destroy an existing task's data. It suggests either picking a free ID or, alternatively, moving the subtask to a different parent as a subtask destination.","triggerScenarios":"Calling moveTask (or moveTaskToTask via moveTask) with a source that is a subtask (sourceId like '5.2') and a numeric destination ID that already exists in tasks, e.g. moving subtask 5.2 to task ID 3 when task 3 exists.","commonSituations":"Auto-picking the destination ID from user input without checking availability; scripts that compute a target ID as max(existing)+1 but run on stale task data; running two move operations concurrently; rerunning a migration script that already created the destination task on a previous run.","solutions":["Pick a destination ID that does not exist: run `tasks.some(t => t.id === destId)` first and choose the next free ID.","If the goal is to keep it nested, pass a subtask destination (parent.subsubtask form) instead of a task ID.","List current tasks to find free IDs, then retry the move with a valid one.","If overwriting is truly intended, delete or move the existing task at that ID first, then retry.","If rerunning a migration, make it idempotent by skipping when the destination already exists."],"exampleFix":"// before\nmoveTask(tasksPath, '5.2', '3'); // task 3 exists -> TASK_ALREADY_EXISTS\n// after\nconst tasks = readJSON(tasksPath);\nconst nextId = Math.max(...tasks.map(t => t.id)) + 1;\nmoveTask(tasksPath, '5.2', String(nextId));","handlingStrategy":"validation","validationCode":"const tasks = JSON.parse(fs.readFileSync(tasksPath, 'utf8')).master.tasks;\nif (tasks.some(t => t.id === Number(destTaskId))) {\n  throw new Error(`Destination ID ${destTaskId} is taken; pick a free ID.`);\n}","typeGuard":"function isFreeTaskId(tasks, id) {\n  return Number.isInteger(id) && !tasks.some(t => t.id === id);\n}","tryCatchPattern":"try {\n  await moveTask(tasksPath, '5.2', destId);\n} catch (e) {\n  if (e.code === 'TASK_ALREADY_EXISTS') {\n    const freeId = Math.max(...getTasks().map(t => t.id)) + 1;\n    await moveTask(tasksPath, '5.2', String(freeId));\n  } else throw e;\n}","preventionTips":["Always compute destination IDs as max(existing)+1 and verify with tasks.some() before moving.","Never hardcode destination task IDs in scripts.","Re-read tasks.json immediately before each move to avoid stale data.","Avoid concurrent move operations against the same tasks.json.","For nested moves, use subtask destinations instead of claiming a top-level ID."],"tags":["task-management","id-conflict","move-task"],"backgroundTag":"task-id-already-exists","analyzedSha":"c0c98d367c55296bfe69e65680625b6db437af02","analyzedAt":"2026-08-29T02:56:26.071Z","schemaVersion":2},"datasetVersion":"2026-08-29T07:17:48.351Z"}