eyaltoledano/claude-task-master · error · Error
Failed to move file from ${oldPath} to ${newPath}: ${error.m
Error message
Failed to move file from ${oldPath} to ${newPath}: ${error.message} What it means
moveFile() uses fs.rename(oldPath, newPath) and wraps any failure into 'Failed to move file from <old> to <new>: <reason>'. rename(2) is atomic but fails across filesystem boundaries (EXDEV) and when source/destination are invalid, so the raw OS error is surfaced with both paths.
Source
Thrown at packages/tm-core/src/modules/storage/adapters/file-storage/file-operations.ts:258
*/
async deleteFile(filePath: string): Promise<void> {
try {
await fs.unlink(filePath);
} catch (error: any) {
if (error.code !== 'ENOENT') {
throw new Error(`Failed to delete file ${filePath}: ${error.message}`);
}
}
}
/**
* Rename/move file
*/
async moveFile(oldPath: string, newPath: string): Promise<void> {
try {
await fs.rename(oldPath, newPath);
} catch (error: any) {
throw new Error(
`Failed to move file from ${oldPath} to ${newPath}: ${error.message}`
);
}
}
/**
* Copy file
*/
async copyFile(srcPath: string, destPath: string): Promise<void> {
try {
await fs.copyFile(srcPath, destPath);
} catch (error: any) {
throw new Error(
`Failed to copy file from ${srcPath} to ${destPath}: ${error.message}`
);
}
}
View on GitHub (pinned to c0c98d367c)
Solutions
- Verify the source file exists at oldPath (ENOENT is the most common cause) and create it first if the flow expects it.
- If the error is EXDEV (cross-device link), copy then delete instead of rename: use copyFile followed by deleteFile.
- Ensure the destination directory exists and is writable (mkdir -p + permissions) before moving.
- Check for the destination being an existing non-empty directory and choose a different destination path or remove it.
Example fix
// before
await fileOps.moveFile('/mnt/vol1/tasks.json', '/mnt/vol2/tasks.json'); // EXDEV
// after
await fileOps.copyFile('/mnt/vol1/tasks.json', '/mnt/vol2/tasks.json');
await fileOps.deleteFile('/mnt/vol1/tasks.json'); Defensive patterns
Strategy: fallback
Validate before calling
import { stat, access, constants } from 'fs/promises';
import path from 'path';
export async function canMoveFile(oldPath: string, newPath: string): Promise<boolean> {
try {
const s = await stat(oldPath);
if (!s.isFile()) return false;
await access(path.dirname(newPath), constants.W_OK);
return true;
} catch { return false; }
} Try / catch
try {
await fileOps.moveFile(oldPath, newPath);
} catch (err: any) {
if (err.message.includes('EXDEV') || err.message.includes('cross-device')) {
// fallback: copy + delete across filesystem boundaries
await fileOps.copyFile(oldPath, newPath);
await fileOps.deleteFile(oldPath);
return;
}
if (err.message.startsWith('Failed to move file')) {
console.error(`Move failed: verify ${oldPath} exists and ${path.dirname(newPath)} is writable (${err.message})`);
}
throw err;
} Prevention
- Keep old and new paths on the same filesystem/mount to avoid EXDEV.
- Ensure the source file exists before rename-based flows (tag rename).
- Pre-create and permission the destination directory.
- Prefer copy+delete as a portable alternative when volumes differ.
When it happens
Trigger: moveFile invoked when the source does not exist (ENOENT), source or destination directory lacks permission (EACCES), old and new paths are on different devices/mounts (EXDEV), or newPath is a non-empty directory (ENOTEMPTY/EEXIST).
Common situations: Renaming a tag file where .taskmaster spans a Docker volume boundary (rename across mounts fails with EXDEV); the source file was never created; destination directory missing or unwritable; Windows file locking by another process.
Related errors
- Failed to read file ${filePath}: ${error.message}
- Failed to read ${filePath} for modification: ${err.message}
- Failed to create directory ${dirPath}: ${error.message}
- Failed to delete file ${filePath}: ${error.message}
- Failed to copy file from ${srcPath} to ${destPath}: ${error.
AI-assisted analysis of eyaltoledano/claude-task-master@c0c98d367c (2026-08-29).
Data as JSON: /api/errors/4c827e8506e6a7ed.
Report an issue: GitHub.