{"record":{"id":"9cb442bc5fec70a7","repo":"eyaltoledano/claude-task-master","slug":"corrupted-json-in-filepath-err-message-fil","errorCode":null,"errorMessage":"Corrupted JSON in ${filePath}: ${err.message}. File contains: ${content.substring(0, 100)}...","messagePattern":"Corrupted JSON in (.+?): (.+?)\\. File contains: (.+?)\\.\\.\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"critical","filePath":"packages/tm-core/src/modules/storage/adapters/file-storage/file-operations.ts","lineNumber":145,"sourceCode":"\t\t\t// Re-read file INSIDE lock to get current state\n\t\t\t// This prevents lost updates from stale snapshots\n\t\t\tlet currentData: T;\n\t\t\ttry {\n\t\t\t\tconst content = await fs.readFile(filePath, 'utf-8');\n\t\t\t\tcurrentData = JSON.parse(content);\n\t\t\t} catch (err: any) {\n\t\t\t\t// Distinguish between expected empty/new files and actual corruption\n\t\t\t\tif (err.code === 'ENOENT') {\n\t\t\t\t\t// File doesn't exist yet - start fresh\n\t\t\t\t\tcurrentData = {} as T;\n\t\t\t\t} else if (err instanceof SyntaxError) {\n\t\t\t\t\t// Check if it's just an empty file (our ensureFileExists writes '{}')\n\t\t\t\t\tconst content = await fs.readFile(filePath, 'utf-8').catch(() => '');\n\t\t\t\t\tif (content.trim() === '' || content.trim() === '{}') {\n\t\t\t\t\t\tcurrentData = {} as T;\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// Actual JSON corruption - this is a serious error\n\t\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t\t`Corrupted JSON in ${filePath}: ${err.message}. File contains: ${content.substring(0, 100)}...`\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\t// Other errors (permission, I/O) should be surfaced\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`Failed to read ${filePath} for modification: ${err.message}`\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Apply modification\n\t\t\tconst newData = await modifier(currentData);\n\n\t\t\t// Write atomically using steno (same pattern as workflow-state-manager)\n\t\t\tconst content = JSON.stringify(newData, null, 2);\n\t\t\tconst writer = this.getWriter(filePath);\n\t\t\tawait writer.write(content);","sourceCodeStart":127,"sourceCodeEnd":163,"githubUrl":"https://github.com/eyaltoledano/claude-task-master/blob/c0c98d367c55296bfe69e65680625b6db437af02/packages/tm-core/src/modules/storage/adapters/file-storage/file-operations.ts#L127-L163","documentation":"During modifyJson's locked read-modify-write, a SyntaxError from JSON.parse triggers a corruption check: if the file content is empty or '{}' it is treated as a fresh file, but any other unparseable content is real corruption and throws immediately rather than silently wiping data. This protects concurrent writers from clobbering a damaged-but-meaningful file.","triggerScenarios":"Any modifyJson consumer (saveTasks, createTag, deleteTag, renameTag, writes, modifyJSON) invoked on an existing .taskmaster JSON file whose content is neither valid JSON nor empty/'{}' — e.g. partial writes from external tools, merge-conflict markers, binary garbage.","commonSituations":"A previous non-atomic write (another tool or an older library version) crashed mid-write; a git merge left conflict markers in tasks.json; disk filled during a write leaving a truncated file; user edited the file with mismatched braces.","solutions":["Inspect the file contents shown after 'File contains:' (first 100 chars) to identify the corruption, then repair or restore it (git checkout -- <file> or from backup).","If the data is recoverable manually, fix the JSON syntax, then re-run the operation.","If the data is expendable, replace the file with '{}' so modifyJson treats it as a fresh store (you lose existing tasks/tags).","Prevent recurrence by only writing these files through the library's atomic writeJson/modifyJson APIs, not external editors or scripts."],"exampleFix":"// before (tasks.json contains merge markers)\n<<<<<<< HEAD\n{ \"tasks\": [] }\n=======\n{ \"tasks\": [{ \"id\": 1 }] }\n>>>>>>> feature\n// after\ngit checkout -- .taskmaster/tasks.json   # or resolve the merge and keep valid JSON","handlingStrategy":"try-catch","validationCode":"import { readFile } from 'fs/promises';\nexport async function isFileHealthy(filePath: string): Promise<boolean> {\n  try {\n    const c = (await readFile(filePath, 'utf-8')).trim();\n    return c === '' || c === '{}' || JSON.parse(c) !== undefined;\n  } catch { return false; }\n}","typeGuard":null,"tryCatchPattern":"try {\n  await fileOps.modifyJson(filePath, (data) => mutate(data));\n} catch (err: any) {\n  if (err.message.startsWith('Corrupted JSON in')) {\n    // do NOT auto-overwrite; prompt user or restore from backup/git\n    await restoreFromBackup(filePath);\n    return fileOps.modifyJson(filePath, (data) => mutate(data));\n  }\n  throw err;\n}","preventionTips":["Write these files only through writeJson/modifyJson (atomic steno writes), never raw fs.writeFile or external scripts.","Keep .taskmaster files committed/backed up so corruption is recoverable.","Resolve merge conflicts before running task commands.","Monitor disk-full conditions that cause truncated writes from other tools."],"tags":["json","corruption","file-storage","data-loss"],"backgroundTag":"corrupted-json-file","analyzedSha":"c0c98d367c55296bfe69e65680625b6db437af02","analyzedAt":"2026-08-29T02:56:26.071Z","schemaVersion":2},"datasetVersion":"2026-08-29T07:17:48.351Z"}