eyaltoledano/claude-task-master · error
Failed to list backups: ${error.message}
Error message
Failed to list backups: ${error.message} What it means
listBackups() reads the backup directory and returns sorted backup filenames. ENOENT (no backup dir yet) is handled gracefully by returning [], but any other readdir failure is wrapped as 'Failed to list backups: <cause>'.
Source
Thrown at packages/tm-core/src/modules/workflow/managers/workflow-state-manager.ts:216
}
}
}
/**
* List available backups
*/
async listBackups(): Promise<string[]> {
try {
const files = await fs.readdir(this.backupDir);
return files
.filter((f) => f.startsWith('workflow-state-') && f.endsWith('.json'))
.sort()
.reverse();
} catch (error: any) {
if (error.code === 'ENOENT') {
return [];
}
throw new Error(`Failed to list backups: ${error.message}`);
}
}
/**
* Restore from a backup
*/
async restoreBackup(backupFileName: string): Promise<void> {
try {
const backupPath = path.join(this.backupDir, backupFileName);
const content = await fs.readFile(backupPath, 'utf-8');
const backup: WorkflowStateBackup = JSON.parse(content);
await this.save(backup.state);
} catch (error: any) {
throw new Error(`Failed to restore backup: ${error.message}`);
}
}
View on GitHub (pinned to c0c98d367c)
Solutions
- Read the cause after the colon to distinguish EACCES vs ENOTDIR etc.
- If ENOTDIR, correct the backupDir configuration to point at a directory.
- Fix read permissions on the backup directory.
- Create the backup directory (with recursive mkdir) so future backups have a home.
- Retry if the storage mount was transiently unavailable.
Example fix
// before
const files = await manager.listBackups(); // ENOTDIR: backupDir is a file
// after
const stat = await fs.stat(backupDir).catch(() => null);
if (!stat?.isDirectory()) await fs.mkdir(backupDir, { recursive: true });
const files = await manager.listBackups(); Defensive patterns
Strategy: fallback
Validate before calling
import { stat } from 'fs/promises';
const s = await stat(backupDir).catch(() => null);
const backupsReadable = s?.isDirectory()
? await access(backupDir, constants.R_OK).then(() => true, () => false)
: false; Type guard
null
Try / catch
let backups = [];
try {
backups = await manager.listBackups();
} catch (e) {
if (!/ENOENT/.test(e.message)) console.error('backup listing failed:', e.message);
// keep [] as fallback
} Prevention
- Ensure backupDir is a directory created at bootstrap
- Keep read permissions consistent for the running user
- Avoid pointing backupDir at files or network mounts that flap
- Default to empty-list handling in UI code
When it happens
Trigger: Calling listBackups() (or the backups accessor) when the backup path exists but is not readable (EACCES), is a file instead of a directory (ENOTDIR), or the readdir otherwise fails.
Common situations: backupDir misconfigured to a regular file; permissions changed after running as another user; network/remote mount temporarily unavailable.
Related errors
- Failed to create backup: ${error.message}
- Workflow state file not found at ${this.statePath}
- Failed to load workflow state: ${error.message}
- Failed to save workflow state: ${error.message}
- Failed to delete workflow state: ${error.message}
AI-assisted analysis of eyaltoledano/claude-task-master@c0c98d367c (2026-08-29).
Data as JSON: /api/errors/162797d1f758f1d3.
Report an issue: GitHub.