eyaltoledano/claude-task-master · warning

Task ${task.id} has unexpected status "${task.status}". Vali

Error message

Task ${task.id} has unexpected status "${task.status}". Valid statuses are: ${TASK_STATUSES.join(', ')}

What it means

task-filters.ts filterReadyTasks() validates each task's status against the known TASK_STATUSES list. An unknown status logs this warning (the task isn't rejected for it) and then the status is checked against ACTIONABLE_STATUSES as usual, so unknown statuses typically get filtered out as non-actionable.

Source

Thrown at packages/tm-core/src/modules/tasks/utils/task-filters.ts:130

 *   { id: '1', status: 'done', dependencies: [], blocks: ['2'] },
 *   { id: '2', status: 'pending', dependencies: ['1'], blocks: [] },
 *   { id: '3', status: 'pending', dependencies: ['2'], blocks: [] }
 * ];
 * const readyTasks = filterReadyTasks(tasks);
 * // Returns only task 2: status is actionable and dependency '1' is done
 * // Task 3 is not ready because dependency '2' is still pending
 * ```
 */
export function filterReadyTasks(tasks: TaskWithBlocks[]): TaskWithBlocks[] {
	// Build set of completed task IDs for dependency checking
	const completedIds = new Set<string>(
		tasks.filter((t) => isTaskComplete(t.status)).map((t) => String(t.id))
	);

	return tasks.filter((task) => {
		// Validate status is a known value
		if (!TASK_STATUSES.includes(task.status)) {
			logger.warn(
				`Task ${task.id} has unexpected status "${task.status}". Valid statuses are: ${TASK_STATUSES.join(', ')}`
			);
		}

		// Must be in an actionable status (excludes deferred, blocked, done, cancelled)
		if (!ACTIONABLE_STATUSES.includes(task.status)) {
			return false;
		}

		// Ready if no dependencies or all dependencies are completed
		const deps = task.dependencies ?? [];
		return deps.every((depId) => completedIds.has(String(depId)));
	});
}

/**
 * Filter to only tasks that block other tasks
 * These are tasks that have at least one other task depending on them

View on GitHub (pinned to c0c98d367c)

Solutions

  1. Update the task's status in tasks.json to a valid value (pending, in-progress, done, deferred, cancelled, blocked, etc. per TASK_STATUSES)
  2. Re-run the version's status migration if the file came from an older release
  3. Fix typos in manually edited status fields
  4. Extend TASK_STATUSES only if you genuinely maintain custom statuses upstream

Example fix

// before
"status": "in review"
// after
"status": "pending"
Defensive patterns

Strategy: type-guard

Validate before calling

import { TASK_STATUSES } from './task-failsafe.js';
const bad = tasks.filter(t => !TASK_STATUSES.includes(t.status));
if (bad.length) console.warn('Tasks with unknown status:', bad.map(t => `${t.id}=${t.status}`));

Type guard

function hasKnownStatus(t: Task): boolean {
  return (TASK_STATUSES as readonly string[]).includes(t.status);
}

Prevention

When it happens

Trigger: filterReadyTasks() (reached via getTasks/readyTasks/tasksToAdd) encounters a task whose status string isn't one of TASK_STATUSES — e.g. legacy statuses like 'review' or 'pending' from older data files, or hand-edited tasks.json.

Common situations: tasks.json created by an older Task Master version before a status rename/migration, manual edits introducing typos ('in-progres'), data merged from other tools with different status vocabularies.

Related errors


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