eyaltoledano/claude-task-master · error
Failed to generate valid JSON from workflow state
Error message
Failed to generate valid JSON from workflow state
What it means
WorkflowStateManager.save() serializes the workflow state to JSON, then defensively parses it back with JSON.parse to prove the string is well-formed before writing. If the round-trip parse fails, the state object contains data JSON.stringify cannot serialize safely (circular refs, BigInt, etc.), so save aborts without touching the file rather than persisting corrupt state.
Source
Thrown at packages/tm-core/src/modules/workflow/managers/workflow-state-manager.ts:144
/**
* Save workflow state to disk
* Uses steno for atomic writes and automatic queueing of concurrent saves
*/
async save(state: WorkflowState): Promise<void> {
try {
// Ensure writer is initialized (creates directory if needed)
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) {View on GitHub (pinned to c0c98d367c)
Solutions
- Inspect the logged jsonContent (printed via logger.error) to find the malformed portion of the serialized state.
- Remove or sanitize non-serializable values (circular refs, BigInt) from the WorkflowState object before calling save.
- If a custom field was added to state, convert it to plain JSON-safe data (e.g. String(bigintValue), plain object copy) before persistence.
- Check for bugs in any override of toJSON on context objects.
Example fix
// before context.session = someCircularSessionObject; await manager.save(state); // after context.session = JSON.parse(JSON.stringify(someCircularSessionObject)); await manager.save(state);
Defensive patterns
Strategy: validation
Validate before calling
function isJsonSafe(value, seen = new Set()) {
if (value === null || typeof value !== 'object') {
if (typeof value === 'bigint') return false;
return true;
}
if (seen.has(value)) return false;
seen.add(value);
return Object.values(value).every((v) => isJsonSafe(v, seen));
}
// before save: if (!isJsonSafe(state)) sanitize state; Type guard
function isPlainJsonSafe<T>(v: T): boolean {
try { JSON.parse(JSON.stringify(v)); return true; } catch { return false; }
} Try / catch
try {
await manager.save(state);
} catch (e) {
if (e.message.includes('valid JSON')) {
state = JSON.parse(JSON.stringify(state, safeReplacer()));
await manager.save(state);
} else throw e;
} Prevention
- Keep WorkflowState limited to plain JSON data — no class instances with circular refs
- Use a replacer function (skip keys, convert BigInt) when serializing custom context
- Unit-test save/restore round-trip for any new state field
- Avoid storing streams, timers, or DOM/Node handles in workflow context
When it happens
Trigger: Calling save() (directly or via startWorkflow, resumeWorkflow, restoreBackup) with a WorkflowState containing circular object references, BigInt values, or a toJSON that throws, producing a string JSON.parse rejects.
Common situations: Custom tooling injects non-serializable objects into workflow context (e.g. a Node.js stream, class instance with circular parent refs, or a BigInt from a JSON-LD parser) before starting or resuming a workflow.
Related errors
- Failed to restore backup: ${error.message}
- Failed to parse JSON response: ${parseError.message}. Respon
- CONFIG_ERROR
- Invalid JSON in file ${filePath}: ${error.message}
- Corrupted JSON in ${filePath}: ${err.message}. File contains
AI-assisted analysis of eyaltoledano/claude-task-master@c0c98d367c (2026-08-29).
Data as JSON: /api/errors/18b8e2503dcd06ea.
Report an issue: GitHub.