CherryHQ/cherry-studio · error · Error
Failed to write file: ${error.message}
Error message
Failed to write file: ${error.message} What it means
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.
Source
Thrown at src/main/ai/mcp/servers/filesystem/tools/write.ts:60
if (error.code !== 'EEXIST') {
throw new Error(`Failed to create parent directory: ${error.message}`)
}
}
// Check if file exists (for logging)
let isOverwrite = false
try {
await fs.stat(validPath)
isOverwrite = true
} catch {
// File doesn't exist, that's fine
}
// Write the file
try {
await fs.writeFile(validPath, parsed.data.content, 'utf-8')
} catch (error: any) {
throw new Error(`Failed to write file: ${error.message}`)
}
// Log the operation
logger.info('File written', {
path: validPath,
overwrite: isOverwrite,
size: parsed.data.content.length
})
// Format output
const relativePath = path.relative(baseDir, validPath)
const action = isOverwrite ? 'Updated' : 'Created'
const lines = parsed.data.content.split('\n').length
return {
content: [
{
type: 'text',View on GitHub (pinned to 726446b54c)
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.
Example fix
// before
await fs.chmod(target, 0o444)
await handleWriteTool({ file_path: target, content: 'x' }, baseDir) // throws: Failed to write file (EACCES)
// after
await fs.chmod(target, 0o644)
await handleWriteTool({ file_path: target, content: 'x' }, baseDir) Defensive patterns
Strategy: try-catch
Validate before calling
import fs from 'fs/promises'
async function canWrite(p: string): Promise<void> {
await fs.access(path.dirname(p), fs.constants.W_OK)
try {
const s = await fs.stat(p)
if (!s.isFile()) throw new Error(`Target is not a regular file: ${p}`)
await fs.access(p, fs.constants.W_OK)
} catch (e: any) { if (e.code !== 'ENOENT') throw e }
} Try / catch
try {
await handleWriteTool(args, baseDir)
} catch (e) {
const msg = e instanceof Error ? e.message : String(e)
if (msg.startsWith('Failed to write file')) {
// EACCES -> chmod/ownership, ENOSPC -> free space, EISDIR -> pick a file path
} else throw e
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Failed to create parent directory: ${error.message}
- Invalid arguments for write: ${parsed.error}
- InternalError
- ${packageLabel} file not found
- Failed to read journal at ${journalPath}: ${(err as Error).m
AI-assisted analysis of CherryHQ/cherry-studio@726446b54c (2026-08-12).
Data as JSON: /api/errors/9c42ec530859c3f8.
Report an issue: GitHub.