{"record":{"id":"162797d1f758f1d3","repo":"eyaltoledano/claude-task-master","slug":"failed-to-list-backups-error-message","errorCode":null,"errorMessage":"Failed to list backups: ${error.message}","messagePattern":"Failed to list backups: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/tm-core/src/modules/workflow/managers/workflow-state-manager.ts","lineNumber":216,"sourceCode":"\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * List available backups\n\t */\n\tasync listBackups(): Promise<string[]> {\n\t\ttry {\n\t\t\tconst files = await fs.readdir(this.backupDir);\n\t\t\treturn files\n\t\t\t\t.filter((f) => f.startsWith('workflow-state-') && f.endsWith('.json'))\n\t\t\t\t.sort()\n\t\t\t\t.reverse();\n\t\t} catch (error: any) {\n\t\t\tif (error.code === 'ENOENT') {\n\t\t\t\treturn [];\n\t\t\t}\n\t\t\tthrow new Error(`Failed to list backups: ${error.message}`);\n\t\t}\n\t}\n\n\t/**\n\t * Restore from a backup\n\t */\n\tasync restoreBackup(backupFileName: string): Promise<void> {\n\t\ttry {\n\t\t\tconst backupPath = path.join(this.backupDir, backupFileName);\n\t\t\tconst content = await fs.readFile(backupPath, 'utf-8');\n\t\t\tconst backup: WorkflowStateBackup = JSON.parse(content);\n\n\t\t\tawait this.save(backup.state);\n\t\t} catch (error: any) {\n\t\t\tthrow new Error(`Failed to restore backup: ${error.message}`);\n\t\t}\n\t}\n","sourceCodeStart":198,"sourceCodeEnd":234,"githubUrl":"https://github.com/eyaltoledano/claude-task-master/blob/c0c98d367c55296bfe69e65680625b6db437af02/packages/tm-core/src/modules/workflow/managers/workflow-state-manager.ts#L198-L234","documentation":"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>'.","triggerScenarios":"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.","commonSituations":"backupDir misconfigured to a regular file; permissions changed after running as another user; network/remote mount temporarily unavailable.","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."],"exampleFix":"// before\nconst files = await manager.listBackups(); // ENOTDIR: backupDir is a file\n// after\nconst stat = await fs.stat(backupDir).catch(() => null);\nif (!stat?.isDirectory()) await fs.mkdir(backupDir, { recursive: true });\nconst files = await manager.listBackups();","handlingStrategy":"fallback","validationCode":"import { stat } from 'fs/promises';\nconst s = await stat(backupDir).catch(() => null);\nconst backupsReadable = s?.isDirectory()\n  ? await access(backupDir, constants.R_OK).then(() => true, () => false)\n  : false;","typeGuard":"null","tryCatchPattern":"let backups = [];\ntry {\n  backups = await manager.listBackups();\n} catch (e) {\n  if (!/ENOENT/.test(e.message)) console.error('backup listing failed:', e.message);\n  // keep [] as fallback\n}","preventionTips":["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"],"tags":["filesystem","backup","workflow"],"backgroundTag":"directory-read-failed","analyzedSha":"c0c98d367c55296bfe69e65680625b6db437af02","analyzedAt":"2026-08-29T02:56:26.071Z","schemaVersion":2},"datasetVersion":"2026-08-29T07:17:48.351Z"}