{"record":{"id":"9c42ec530859c3f8","repo":"CherryHQ/cherry-studio","slug":"failed-to-write-file-error-message","errorCode":null,"errorMessage":"Failed to write file: ${error.message}","messagePattern":"Failed to write file: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/main/ai/mcp/servers/filesystem/tools/write.ts","lineNumber":60,"sourceCode":"    if (error.code !== 'EEXIST') {\n      throw new Error(`Failed to create parent directory: ${error.message}`)\n    }\n  }\n\n  // Check if file exists (for logging)\n  let isOverwrite = false\n  try {\n    await fs.stat(validPath)\n    isOverwrite = true\n  } catch {\n    // File doesn't exist, that's fine\n  }\n\n  // Write the file\n  try {\n    await fs.writeFile(validPath, parsed.data.content, 'utf-8')\n  } catch (error: any) {\n    throw new Error(`Failed to write file: ${error.message}`)\n  }\n\n  // Log the operation\n  logger.info('File written', {\n    path: validPath,\n    overwrite: isOverwrite,\n    size: parsed.data.content.length\n  })\n\n  // Format output\n  const relativePath = path.relative(baseDir, validPath)\n  const action = isOverwrite ? 'Updated' : 'Created'\n  const lines = parsed.data.content.split('\\n').length\n\n  return {\n    content: [\n      {\n        type: 'text',","sourceCodeStart":42,"sourceCodeEnd":78,"githubUrl":"https://github.com/CherryHQ/cherry-studio/blob/726446b54cd69ffe51a276638672f6d95ca0768c/src/main/ai/mcp/servers/filesystem/tools/write.ts#L42-L78","documentation":"Thrown by the write tool when fs.writeFile(validPath, content, 'utf-8') rejects. By this point the parent directory was created (or already existed), so the failure is at the file-write itself: EACCES (no write permission on the file/path), EISDIR (target is a directory), ENOSPC (disk full), EROFS (read-only fs), EDQUOT (quota exceeded), or EBADF. The underlying message is appended verbatim.","triggerScenarios":"Overwriting a read-only or permission-locked file; writing where validPath resolves to a directory (EISDIR, despite the earlier stat-based isOverwrite check racing); disk full mid-write; quota exceeded; file deleted between the stat and writeFile.","commonSituations":"CI/container with a read-only output layer; file owned by another user with mode 0444; large content exceeding disk quota; concurrent process removed the file after the stat check; NFS/SMB mount with delayed permissions.","solutions":["Verify write permission on the target file/path (fs.access with W_OK) before calling write.","Free disk space or raise the user quota if error.message mentions ENOSPC/EDQUOT.","If the target is a directory, choose a file path instead (EISDIR).","Handle the race by catching EACCES/EISDIR and reporting the resolved path to the user."],"exampleFix":"// before\nawait fs.chmod(target, 0o444)\nawait handleWriteTool({ file_path: target, content: 'x' }, baseDir) // throws: Failed to write file (EACCES)\n\n// after\nawait fs.chmod(target, 0o644)\nawait handleWriteTool({ file_path: target, content: 'x' }, baseDir)","handlingStrategy":"try-catch","validationCode":"import fs from 'fs/promises'\nasync function canWrite(p: string): Promise<void> {\n  await fs.access(path.dirname(p), fs.constants.W_OK)\n  try {\n    const s = await fs.stat(p)\n    if (!s.isFile()) throw new Error(`Target is not a regular file: ${p}`)\n    await fs.access(p, fs.constants.W_OK)\n  } catch (e: any) { if (e.code !== 'ENOENT') throw e }\n}","typeGuard":null,"tryCatchPattern":"try {\n  await handleWriteTool(args, baseDir)\n} catch (e) {\n  const msg = e instanceof Error ? e.message : String(e)\n  if (msg.startsWith('Failed to write file')) {\n    // EACCES -> chmod/ownership, ENOSPC -> free space, EISDIR -> pick a file path\n  } else throw e\n}","preventionTips":["Check disk space and quota before large writes.","Keep output files writable by the app process (mode 0o644 or stricter with the right owner).","Treat EROFS as a deployment-config problem: remount or relocate the workspace."],"tags":["filesystem","permissions","mcp","write-tool","enospc"],"backgroundTag":null,"analyzedSha":"726446b54cd69ffe51a276638672f6d95ca0768c","analyzedAt":"2026-08-12T17:30:37.448Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}