eyaltoledano/claude-task-master · warning
TASK_COMPLETED
TASK_COMPLETED
Error message
Task ${taskId} is already marked as ${task.status} and cannot be expanded What it means
expandTaskDirect refuses to expand a task that is already marked 'done' or 'completed'. Task Master treats expansion as a planning operation only valid for open work; expanding a finished task would generate meaningless subtasks. The MCP tool returns a structured {success:false, error:{code:'TASK_COMPLETED'}} result instead of throwing.
Source
Thrown at mcp-server/src/core/direct-functions/expand-task.js:141
const task = data.tasks.find((t) => t.id === taskId);
log.info(`[expandTaskDirect] Task found: ${task ? 'Yes' : 'No'}`);
if (!task) {
return {
success: false,
error: {
code: 'TASK_NOT_FOUND',
message: `Task with ID ${taskId} not found`
}
};
}
// Check if task is completed
if (task.status === 'done' || task.status === 'completed') {
return {
success: false,
error: {
code: 'TASK_COMPLETED',
message: `Task ${taskId} is already marked as ${task.status} and cannot be expanded`
}
};
}
// Check for existing subtasks and force flag
const hasExistingSubtasks = task.subtasks && task.subtasks.length > 0;
if (hasExistingSubtasks && !forceFlag) {
log.info(
`Task ${taskId} already has ${task.subtasks.length} subtasks. Use --force to overwrite.`
);
return {
success: true,
data: {
message: `Task ${taskId} already has subtasks. Expansion skipped.`,
task,
subtasksAdded: 0,
hasExistingSubtasksView on GitHub (pinned to c0c98d367c)
Solutions
- Check task.status before calling expand_task and skip tasks marked done/completed
- Pick a different open task ID to expand
- If the task truly needs rework, set its status back to 'pending' (or via update_task_status) then re-run expand_task
- If subtasks already exist and you intended regeneration, use force:true instead — but that still requires a non-completed task
Example fix
// before
await mcp.call('expand_task', { projectId, taskId: '5' }); // task 5 is done
// after
const task = await mcp.call('get_task', { projectId, taskId: '5' });
if (task.status !== 'done' && task.status !== 'completed') {
await mcp.call('expand_task', { projectId, taskId: '5' });
} Defensive patterns
Strategy: validation
Validate before calling
const task = await getTask(projectRoot, taskId);
if (task.status === 'done' || task.status === 'completed') {
throw new Error(`Skip expand_task: task ${taskId} is ${task.status}`);
} Type guard
function isExpandable(task) {
return typeof task === 'object' && task !== null &&
task.status !== 'done' && task.status !== 'completed';
} Try / catch
try {
const res = await callTool('expand_task', { taskId, projectRoot });
if (!res.success && res.error?.code === 'TASK_COMPLETED') {
console.warn(`Task ${taskId} already completed; skipping expansion`);
}
} catch (e) { console.error(e.message); } Prevention
- Fetch task status before every expand call in loops
- Filter task lists to pending/in-progress before expanding
- Use force:true only deliberately, and never on completed tasks
- Cache task state briefly but revalidate before mutating calls
When it happens
Trigger: Calling the expand_task MCP tool with a taskId whose status is 'done' or 'completed' in tasks.json (and no early bypass).
Common situations: AI agents iterating a task list after finishing work and forgetting to skip completed IDs; stale client caches pointing at a task another session closed; replaying an old expand call after the task was marked complete.
Related errors
- Generated object does not match schema: ${validationError.me
- Task description is required
- Task status is required
- INVALID_SOURCE_TAG
- Parent task ${parentId} has no subtasks.
AI-assisted analysis of eyaltoledano/claude-task-master@c0c98d367c (2026-08-29).
Data as JSON: /api/errors/9ee64197155a072b.
Report an issue: GitHub.