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

  1. Read the cause after the colon to distinguish EACCES vs ENOTDIR etc.
  2. If ENOTDIR, correct the backupDir configuration to point at a directory.
  3. Fix read permissions on the backup directory.
  4. Create the backup directory (with recursive mkdir) so future backups have a home.
  5. 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

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


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