eyaltoledano/claude-task-master · critical
Failed to save workflow state: ${error.message}
Error message
Failed to save workflow state: ${error.message} What it means
This is the catch-all wrapper around WorkflowStateManager.save(). Any failure during serialization, validation, or the steno atomic file write is rethrown as 'Failed to save workflow state: <cause>'. The original error message is preserved after the colon, so read it to find the root cause (disk full, permissions, invalid JSON).
Source
Thrown at packages/tm-core/src/modules/workflow/managers/workflow-state-manager.ts:152
await this.ensureWriter();
// Serialize and validate JSON
const jsonContent = JSON.stringify(state, null, 2);
// Validate that the JSON is well-formed by parsing it back
try {
JSON.parse(jsonContent);
} catch (parseError) {
this.logger.error('Generated invalid JSON:', jsonContent);
throw new Error('Failed to generate valid JSON from workflow state');
}
// Write using steno (handles queuing and atomic writes automatically)
await this.writer!.write(jsonContent + '\n');
this.logger.debug(`Saved workflow state (${jsonContent.length} bytes)`);
} catch (error: any) {
throw new Error(`Failed to save workflow state: ${error.message}`);
}
}
/**
* Create a backup of current state
*/
async createBackup(): Promise<void> {
try {
const exists = await this.exists();
if (!exists) {
return;
}
const state = await this.load();
await fs.mkdir(this.backupDir, { recursive: true });
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
const backupPath = path.join(View on GitHub (pinned to c0c98d367c)
Solutions
- Read the cause text after 'Failed to save workflow state:' to identify the underlying error.
- Check filesystem permissions and free disk space on the directory containing the state file.
- Verify the state object is JSON-serializable (no circular refs/BigInt).
- Ensure the state directory exists and the process has write access; create it manually if missing.
- Retry if the failure was transient (e.g. temporary EBUSY/EPERM during sync tooling).
Example fix
// before
await manager.save(state); // throws opaque wrapped error
// after
try {
await manager.save(state);
} catch (e) {
console.error('root cause:', e.message); // e.g. EACCES: permission denied
fs.mkdirSync(path.dirname(statePath), { recursive: true });
await manager.save(state);
} Defensive patterns
Strategy: try-catch
Validate before calling
import { accessSync, constants, mkdirSync } from 'fs';
const dir = path.dirname(statePath);
try { accessSync(dir, constants.W_OK); } catch { mkdirSync(dir, { recursive: true }); } Type guard
null
Try / catch
try {
await manager.save(state);
} catch (e) {
const cause = e.message.replace('Failed to save workflow state: ', '');
if (/EACCES|EPERM/.test(cause)) fixPermissions(dir);
else if (/ENOSPC/.test(cause)) freeDiskSpace();
else if (/invalid JSON/.test(cause)) sanitizeState(state);
throw e;
} Prevention
- Verify write access to the state directory at startup
- Monitor disk space on volumes holding state files
- Keep state JSON-serializable
- Wrap save calls at a higher level with one retry after fixing environment
When it happens
Trigger: Any save() call (also via startWorkflow, resumeWorkflow, restoreBackup) where JSON.stringify/parse fails or this.writer.write() rejects — e.g. the state directory is read-only, disk is full, or steno write throws.
Common situations: Read-only filesystem or wrong permissions on the workflow state path; ENOSPC on a full disk; state file locked by another process; invalid JSON from non-serializable state (error 240's cause wrapped here).
Related errors
- SAVE_FAILED
- CONFIG_ERROR
- Workflow state file not found at ${this.statePath}
- Failed to load 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/12302c9f5d99b943.
Report an issue: GitHub.