eyaltoledano/claude-task-master · error
Failed to delete workflow state: ${error.message}
Error message
Failed to delete workflow state: ${error.message} What it means
delete() removes the workflow state file with fs.unlink. A missing file (ENOENT) is treated as success, but any other unlink failure — permissions, directory-is-file mismatch, EBUSY — is wrapped as 'Failed to delete workflow state: <cause>'.
Source
Thrown at packages/tm-core/src/modules/workflow/managers/workflow-state-manager.ts:197
await fs.writeFile(backupPath, JSON.stringify(backup, null, 2), 'utf-8');
// Clean up old backups
await this.pruneBackups();
} catch (error: any) {
throw new Error(`Failed to create backup: ${error.message}`);
}
}
/**
* Delete workflow state file
*/
async delete(): Promise<void> {
try {
await fs.unlink(this.statePath);
} catch (error: any) {
if (error.code !== 'ENOENT') {
throw new Error(`Failed to delete workflow state: ${error.message}`);
}
}
}
/**
* 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 [];
}View on GitHub (pinned to c0c98d367c)
Solutions
- Read the cause after the colon (EACCES/EPERM/EBUSY etc.).
- Check and correct permissions/ownership on the state file and its parent directory.
- Close processes holding the file open (editors, sync clients, antivirus on Windows).
- If EISDIR, fix the statePath configuration — it must point to a file.
- Manually remove the file if the process context cannot (e.g. container user mismatch).
Example fix
// before
await manager.delete(); // EACCES under container user
// after
try {
await manager.delete();
} catch (e) {
if (/EACCES|EPERM/.test(e.message)) {
await exec(`sudo rm -f ${statePath}`); // or fix ownership: chown
}
} Defensive patterns
Strategy: try-catch
Validate before calling
import { stat, access, constants } from 'fs/promises';
try {
const s = await stat(statePath);
if (!s.isFile()) throw new Error('statePath must be a file');
await access(path.dirname(statePath), constants.W_OK);
} catch { /* missing file = delete is a no-op, fine */ } Type guard
null
Try / catch
try {
await manager.delete();
} catch (e) {
if (/EACCES|EPERM|EBUSY/.test(e.message)) {
console.error('State file locked or not removable:', e.message);
}
throw e;
} Prevention
- Run all workflow processes under the same user to avoid ownership mismatches
- Exclude state files from AV/backup locks on Windows
- Configure statePath to a file, never a directory
- Treat ENOENT as success (the manager already does)
When it happens
Trigger: Calling delete() when statePath points to a file the process lacks permission to remove, the path is a non-empty directory, or the file is locked/busy (EPERM, EACCES, EBUSY, EISDIR).
Common situations: State file owned by root or another user after sudo runs; read-only mounted volume; Windows file lock from an editor or AV scanner; statePath misconfigured to a directory.
Related errors
- Failed to delete file ${filePath}: ${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 create backup: ${error.message}
AI-assisted analysis of eyaltoledano/claude-task-master@c0c98d367c (2026-08-29).
Data as JSON: /api/errors/f56ef7597366849e.
Report an issue: GitHub.