eyaltoledano/claude-task-master · critical
Failed to restore backup: ${error.message}
Error message
Failed to restore backup: ${error.message} What it means
restoreBackup() reads a backup file, JSON.parses it into a WorkflowStateBackup, and writes its state back via save(). Any failure at any of those steps — missing backup file, corrupt backup JSON, or a save() failure — is wrapped as 'Failed to restore backup: <cause>'.
Source
Thrown at packages/tm-core/src/modules/workflow/managers/workflow-state-manager.ts:231
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}`);
}
}
/**
* Prune old backups to maintain max backup count
*/
private async pruneBackups(): Promise<void> {
try {
const backups = await this.listBackups();
if (backups.length > this.maxBackups) {
const toDelete = backups.slice(this.maxBackups);
for (const backup of toDelete) {
await fs.unlink(path.join(this.backupDir, backup));
}
}
} catch (error: any) {View on GitHub (pinned to c0c98d367c)
Solutions
- Read the cause after the colon: ENOENT means the backup name is wrong; SyntaxError means corrupt JSON; 'Failed to save workflow state' means the write step failed.
- Run listBackups() to confirm the exact backup filename exists before restoring.
- If JSON is corrupt, choose a different (older) backup file.
- Validate the backup's JSON manually (jq <file>) to inspect damage.
- Fix filesystem permissions/space if the underlying save() failed.
Example fix
// before
await manager.restoreBackup('state-backup-2026-08-27T10.json'); // guess
// after
const backups = await manager.listBackups();
if (backups.includes('state-backup-2026-08-27T10.json')) {
await manager.restoreBackup('state-backup-2026-08-27T10.json');
} Defensive patterns
Strategy: validation
Validate before calling
import { access } from 'fs/promises';
const backupPath = path.join(backupDir, fileName);
await access(backupPath); // throws ENOENT early if backup missing
const content = await readFile(backupPath, 'utf-8');
JSON.parse(content); // throws SyntaxError early if corrupt
if (!('state' in JSON.parse(content))) throw new Error('backup missing state'); Type guard
function isWorkflowStateBackup(v) {
return v !== null && typeof v === 'object'
&& 'state' in v
&& typeof v.state === 'object'
&& v.state !== null;
} Try / catch
try {
await manager.restoreBackup(fileName);
} catch (e) {
if (/ENOENT/.test(e.message)) {
const alt = (await manager.listBackups())[0];
if (alt) await manager.restoreBackup(alt);
} else throw e;
} Prevention
- Always enumerate backups with listBackups() before restoring
- Validate backup JSON (jq or JSON.parse) after any interrupted write
- Keep several backup generations; never rely on a single file
- Verify backup schema (has .state) before restore
When it happens
Trigger: Calling restoreBackup(fileName) with a backup file that does not exist (ENOENT), contains truncated/invalid JSON (SyntaxError from JSON.parse), or whose inner save() fails (disk/permission issues, error 241).
Common situations: Restoring from a backup list captured before a prune removed the file; backup interrupted mid-write leaving truncated JSON; manual edits corrupting the backup; backup produced by an older schema that save() cannot persist.
Related errors
- Failed to generate valid JSON from workflow state
- Failed to create backup: ${error.message}
- Failed to list backups: ${error.message}
- Failed to parse JSON response: ${parseError.message}. Respon
- CONFIG_ERROR
AI-assisted analysis of eyaltoledano/claude-task-master@c0c98d367c (2026-08-29).
Data as JSON: /api/errors/f3a26bd505170a76.
Report an issue: GitHub.