eyaltoledano/claude-task-master · error · Error

Invalid JSON in file ${filePath}: ${error.message}

Error message

Invalid JSON in file ${filePath}: ${error.message}

What it means

readJson() parses a file with JSON.parse; when parsing fails with a SyntaxError the file exists but is not valid JSON, so a descriptive Error naming the file and parse message is thrown. ENOENT is deliberately re-thrown so callers can treat a missing file as 'no data' rather than corruption.

Source

Thrown at packages/tm-core/src/modules/storage/adapters/file-storage/file-operations.ts:63

			writer = new Writer(filePath);
			this.writers.set(filePath, writer);
		}
		return writer;
	}

	/**
	 * Read and parse JSON file
	 */
	async readJson(filePath: string): Promise<any> {
		try {
			const content = await fs.readFile(filePath, 'utf-8');
			return JSON.parse(content);
		} catch (error: any) {
			if (error.code === 'ENOENT') {
				throw error; // Re-throw ENOENT for caller to handle
			}
			if (error instanceof SyntaxError) {
				throw new Error(`Invalid JSON in file ${filePath}: ${error.message}`);
			}
			throw new Error(`Failed to read file ${filePath}: ${error.message}`);
		}
	}

	/**
	 * Write JSON file with atomic operation and cross-process locking.
	 * Uses steno for atomic writes and proper-lockfile for cross-process safety.
	 * WARNING: This replaces the entire file. For concurrent modifications,
	 * use modifyJson() instead to prevent lost updates.
	 */
	async writeJson(
		filePath: string,
		data: FileStorageData | any
	): Promise<void> {
		// Ensure file exists for locking (proper-lockfile requires this)
		await this.ensureFileExists(filePath);

View on GitHub (pinned to c0c98d367c)

Solutions

  1. Open the file at the path in the message and fix the JSON syntax indicated by the parse error (line/column in error.message).
  2. If the file is unimportant or regenerable, restore it from git (git checkout -- <file>) or delete it so the storage layer recreates it.
  3. Validate the file with a parser (node -e "JSON.parse(require('fs').readFileSync('<path>','utf8'))") after editing to confirm it parses.
  4. Avoid hand-editing storage files while the app runs; use the CLI commands that go through modifyJson/writeJson atomic writes.

Example fix

// before (tasks.json contains a trailing comma)
{ "tasks": [ ... ], }
// after
{
  "tasks": [ ... ]
}
Defensive patterns

Strategy: validation

Validate before calling

import { readFileSync } from 'fs';
export function isValidJsonFile(filePath: string): boolean {
  try { JSON.parse(readFileSync(filePath, 'utf-8')); return true; }
  catch (err: any) { return err.code === 'ENOENT'; } // missing is OK, malformed is not
}

Try / catch

try {
  const data = await fileOps.readJson(filePath);
} catch (err: any) {
  if (err.code === 'ENOENT') return defaultValue;
  if (err.message.startsWith('Invalid JSON in file')) {
    // offer backup restore or interactive repair
    return recoverFromBackup(filePath) ?? defaultValue;
  }
  throw err;
}

Prevention

When it happens

Trigger: Calling readJson (directly or via data/rawData/stateData accessors) on a file whose contents are truncated, hand-edited incorrectly, contain trailing commas/comments, or were written by another tool in a non-JSON format.

Common situations: A crashed or interrupted write left a partial file (outside this library's atomic steno writes); a user manually edited .taskmaster/tasks.json and broke syntax; merge conflict markers (<<<<<<<) left in the file; the file was saved with BOM or as YAML/JSON5.

Understand the failure class

Related errors


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