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 themView on GitHub (pinned to c0c98d367c)
Solutions
- Update the task's status in tasks.json to a valid value (pending, in-progress, done, deferred, cancelled, blocked, etc. per TASK_STATUSES)
- Re-run the version's status migration if the file came from an older release
- Fix typos in manually edited status fields
- 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
- Only set statuses from the documented TASK_STATUSES enum
- Run the status migration when upgrading versions
- Schema-validate tasks.json (status enum) in CI
- Avoid free-text statuses in manual edits and external imports
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
- Invalid iterations: ${iterations}. Must be a positive intege
- Invalid format: ${options.format}. Valid formats are: text,
- Cannot make a task a subtask of itself
- MFA_VERIFICATION_FAILED
- Failed to initialize services: ${(error as Error).message}
AI-assisted analysis of eyaltoledano/claude-task-master@c0c98d367c (2026-08-29).
Data as JSON: /api/errors/1a7d16899fbe952b.
Report an issue: GitHub.