eyaltoledano/claude-task-master · critical · Error
Failed to load tasks: ${error.message}
Error message
Failed to load tasks: ${error.message} What it means
loadTasks() reads the single tasks.json file for a tag and enriches tasks with complexity data. ENOENT is treated as 'no tasks yet' (empty array); any other error — JSON corruption, permissions, enrichment failures — is wrapped and rethrown as 'Failed to load tasks'.
Source
Thrown at packages/tm-core/src/modules/storage/adapters/file-storage/file-storage.ts:153
if (options.status) {
tasks = tasks.filter((task) => task.status === options.status);
}
// Exclude subtasks if specified
if (options.excludeSubtasks) {
tasks = tasks.map((task) => ({
...task,
subtasks: []
}));
}
}
return await this.enrichTasksWithComplexity(tasks, resolvedTag);
} catch (error: any) {
if (error.code === 'ENOENT') {
return []; // File doesn't exist, return empty array
}
throw new Error(`Failed to load tasks: ${error.message}`);
}
}
/**
* Load a single task by ID from the tasks.json file
* Handles both regular tasks and subtasks (with dotted notation like "1.2")
*/
async loadTask(taskId: string, tag?: string): Promise<Task | null> {
const tasks = await this.loadTasks(tag);
// Check if this is a subtask (contains a dot)
if (taskId.includes('.')) {
const [parentId, subtaskId] = taskId.split('.');
const parentTask = tasks.find((t) => String(t.id) === parentId);
if (!parentTask || !parentTask.subtasks) {
return null;
}View on GitHub (pinned to c0c98d367c)
Solutions
- Inspect error.message in the thrown error for the root cause (parse vs permission)
- Validate and repair tasks.json JSON syntax or restore it from version control
- Fix permissions: chmod u+r .taskmaster/tasks.json
- If the file should not exist yet, delete the corrupt file so ENOENT handling returns an empty task list
Example fix
// before
const tasks = await tmCore.tasks.get(); // throws 'Failed to load tasks: Unexpected token...'
// after: guard the environment first
import { existsSync, readFileSync } from 'fs';
const p = '.taskmaster/tasks.json';
if (existsSync(p)) JSON.parse(readFileSync(p, 'utf8')); // fail fast with a clear parse error
const tasks = await tmCore.tasks.get(); Defensive patterns
Strategy: try-catch
Validate before calling
import { existsSync } from 'fs';
function tasksFileLooksValid(path = '.taskmaster/tasks.json') {
if (!existsSync(path)) return true; // ENOENT => empty list is fine
try { const d = JSON.parse(readFileSync(path, 'utf8')); return typeof d === 'object' && d !== null; }
catch { return false; }
} Type guard
function isLoadTasksError(e: unknown): e is Error {
return e instanceof Error && e.message.startsWith('Failed to load tasks:');
} Try / catch
try {
const tasks = await storage.loadTasks(tag);
} catch (e) {
if (isLoadTasksError(e)) {
// surface the inner cause: e.message.split('Failed to load tasks: ')[1]
// offer repair/restore UX or restore from backup before retrying
} else throw e;
} Prevention
- Use atomic writes (write temp file + rename) when modifying tasks.json yourself
- Validate JSON after manual edits with a linter
- Back up tasks.json before bulk operations
- Check file permissions after cloning repos across users
When it happens
Trigger: tasks.json exists but contains invalid JSON, EACCES/EACCES-style permission denial, tasks.json is a directory, or enrichTasksWithComplexity throws while merging complexity report data into the loaded tasks.
Common situations: A partially written tasks.json from a crashed process or interrupted save, a complexity report referencing malformed data, repo cloned without read permissions, or a symlink pointing at a nonexistent/unreadable target.
Related errors
- Failed to get storage stats: ${error.message}
- Failed to get tags: ${error.message}
- Failed to load metadata: ${error.message}
- Task ID is required
- Task title is required
AI-assisted analysis of eyaltoledano/claude-task-master@c0c98d367c (2026-08-29).
Data as JSON: /api/errors/c2eb57818a1df6a6.
Report an issue: GitHub.