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

  1. Inspect the logged jsonContent (printed via logger.error) to find the malformed portion of the serialized state.
  2. Remove or sanitize non-serializable values (circular refs, BigInt) from the WorkflowState object before calling save.
  3. If a custom field was added to state, convert it to plain JSON-safe data (e.g. String(bigintValue), plain object copy) before persistence.
  4. 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

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


AI-assisted analysis of eyaltoledano/claude-task-master@c0c98d367c (2026-08-29). Data as JSON: /api/errors/18b8e2503dcd06ea. Report an issue: GitHub.