eyaltoledano/claude-task-master · error · Error

Failed to read file ${filePath}: ${error.message}

Error message

Failed to read file ${filePath}: ${error.message}

What it means

readJson() wraps any read failure that is neither ENOENT nor a JSON SyntaxError into 'Failed to read file <path>: <reason>'. This covers filesystem-level problems (permissions, EISDIR, EACCES, I/O errors) encountered while reading the file, as opposed to parse problems.

Source

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

		}
		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);

		// Acquire cross-process lock
		let release: (() => Promise<void>) | null = null;

View on GitHub (pinned to c0c98d367c)

Solutions

  1. Check the underlying reason in error.message (e.g. EACCES, EISDIR) and fix it — correct the path if it points at a directory.
  2. Fix permissions: chmod/chown the file or its parent directory so the running user can read it.
  3. Verify the storage root path configuration points to the intended directory containing the JSON files.
  4. If running in Docker/CI, mount the .taskmaster directory with correct ownership (match the container user's UID/GID).

Example fix

// before
ls -l ~/.taskmaster/tasks.json  # -rw------- root root
// after
sudo chown $(whoami) ~/.taskmaster/tasks.json
chmod u+rw ~/.taskmaster/tasks.json
Defensive patterns

Strategy: try-catch

Validate before calling

import { access, constants } from 'fs/promises';
export async function isReadableFile(filePath: string): Promise<boolean> {
  try { await access(filePath, constants.R_OK); return true; } catch { return false; }
}

Try / catch

try {
  const data = await fileOps.readJson(filePath);
} catch (err: any) {
  if (err.code === 'ENOENT') return defaultValue;
  if (err.message.startsWith('Failed to read file')) {
    console.error(`Cannot read ${filePath}: check permissions/path (${err.message})`);
    throw err;
  }
  throw err;
}

Prevention

When it happens

Trigger: Calling readJson (or data/rawData/stateData accessors) when fs.readFile fails for a non-missing-file reason: the path is a directory, the process lacks read permission, the file is on a detached/unmounted volume, or an EBUSY/EIO condition occurs.

Common situations: Running under a different user (CI, Docker) without permissions on ~/.taskmaster; the configured storage path accidentally points at a directory; a Windows file lock held by another process; NFS/disk errors in containerized environments.

Related errors


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