Lỗi
Error: EPERM: operation not permitted, rename 'C:\project\file.tmp' -> 'C:\project\file.js'
Node.js cố đổi tên một file. Windows không cho phép. Đó là phiên bản ngắn gọn.
Phiên bản đầy đủ hơn: có thứ gì đó đang khóa file — một trình quét chạy nền, một tiến trình zombie, hoặc chính code của bạn quên đóng stream trước. Lỗi này thường xuất hiện nhất trong quá trình build, npm install, hoặc bất kỳ công cụ nào ghi file theo kiểu temp-file-rồi-rename (webpack, rollup, esbuild, fs-extra, và các công cụ tương tự đều làm vậy).
Nguyên nhân gốc rễ
Windows sử dụng cơ chế khóa file bắt buộc. Trên Linux, bạn có thể đổi tên một file đang mở mà không gặp vấn đề gì. Trên Windows thì không — bất kỳ tiến trình nào đang giữ file mở đều chặn thao tác đổi tên cho đến khi nó nhả ra. Các "nghi phạm" thường gặp:
- Antivirus / Windows Defender — quét file ngay khi file vừa được ghi xuống đĩa, đúng lúc Node đang cố đổi tên
- Một tiến trình Node khác — một watcher, dev server, hoặc tiến trình zombie vẫn đang giữ file handle
- Windows Search indexer — âm thầm quét thư mục dự án của bạn trong nền
- Chính code của bạn — gọi
fs.renametrước khi write stream đã đóng hoàn toàn - Không đủ quyền — Node chạy mà không có quyền ghi vào thư mục đích
Cách sửa 1: Loại trừ thư mục dự án khỏi Antivirus (Cách phổ biến nhất)
Trong hầu hết các trường hợp, Windows Defender là thủ phạm. Nó quét file mới ngay khi file vừa xuất hiện trên đĩa — đúng lúc Node đang cố đổi tên. Khoảng thời gian này rất ngắn (thường dưới 50ms), nhưng đủ để gây ra lỗi EPERM.
Thêm thư mục dự án vào danh sách loại trừ:
- Mở Windows Security → Virus & threat protection → Manage settings
- Kéo xuống Exclusions → Add or remove exclusions
- Thêm loại trừ Folder cho thư mục gốc của dự án (ví dụ:
C:\projects\myapp) - Cũng loại trừ
node_modulesvà các thư mụcdist/buildnếu chúng nằm ngoài thư mục gốc dự án
Các phần mềm antivirus bên thứ ba (Kaspersky, Bitdefender, v.v.) có cùng thiết lập này trong phần loại trừ của bảo vệ thời gian thực.
Cách sửa 2: Tắt các tiến trình Node zombie
Một dev server bị crash không phải lúc nào cũng tự dọn dẹp sau khi thoát. Tiến trình đã biến mất khỏi Task Manager, nhưng Windows vẫn đánh dấu các file handle của nó là đang được sử dụng. Hãy tắt tất cả và bắt đầu lại từ đầu:
taskkill /F /IM node.exe
Cần chính xác hơn? PowerShell cho phép bạn chọn tiến trình nào cần dừng:
Get-Process node | Stop-Process -Force
Sau khi đã dọn sạch, chạy lại lệnh build hoặc install của bạn.
Cách sửa 3: Thêm logic retry trong code của bạn
Khóa từ Defender hoặc indexer thường được giải phóng trong vòng 200ms. Thay vì crash ngay lập tức, hãy thử lại với exponential backoff — tăng gấp đôi độ trễ mỗi lần thử, để tránh liên tục tấn công một file vẫn còn bị khóa:
const fs = require('fs');
function renameWithRetry(oldPath, newPath, retries = 5, delay = 100) {
return new Promise((resolve, reject) => {
fs.rename(oldPath, newPath, (err) => {
if (!err) return resolve();
if (err.code === 'EPERM' && retries > 0) {
setTimeout(() => {
renameWithRetry(oldPath, newPath, retries - 1, delay * 2)
.then(resolve)
.catch(reject);
}, delay);
} else {
reject(err);
}
});
});
}
// Cách dùng
await renameWithRetry('output.tmp', 'output.js');
Với 5 lần thử lại và độ trễ ban đầu 100ms, bạn có tổng cộng khoảng ~3.1 giây chờ đợi trước khi từ bỏ — đủ để vượt qua một khóa tạm thời từ trình quét.
Cách sửa 4: Tắt Windows Search Indexing cho thư mục dự án
Search indexer là thủ phạm ít bị chú ý hơn nhưng gây ra lỗi EPERM tương tự trong các thao tác ghi nặng như npm install vào một monorepo lớn:
- Mở File Explorer → chuột phải vào thư mục dự án → Properties
- Trong tab General, nhấn Advanced
- Bỏ chọn Allow files in this folder to have contents indexed
- Áp dụng cho tất cả thư mục con khi được hỏi
Cách sửa 5: Chạy với quyền nâng cao (Phương án cuối cùng)
Ghi vào đường dẫn được bảo vệ như C:\Program Files hoặc thư mục thuộc hệ thống? Node cần quyền Administrator:
# Mở Command Prompt với quyền Administrator, sau đó:
node build.js
Với npm scripts, chuột phải vào terminal và chọn "Run as administrator". Hãy coi đây là phương án cuối cùng — nếu thư mục đích là do bạn tự tạo, hãy khắc phục nguyên nhân gốc thay vì leo thang quyền hạn.
Cách sửa 6: Đảm bảo file đã đóng trước khi đổi tên
Nếu chính code của bạn gây ra khóa, cách sửa rất đơn giản: hãy chờ stream đóng hoàn toàn trước khi gọi rename. Callback end là tín hiệu để bạn biết:
const fs = require('fs');
const stream = fs.createWriteStream('output.tmp');
stream.write('hello world');
stream.end(() => {
// File handle đã được giải phóng ở đây — an toàn để đổi tên
fs.rename('output.tmp', 'output.js', (err) => {
if (err) console.error('Rename failed:', err);
});
});
Gọi fs.rename bên trong callback end đảm bảo file handle đã được giải phóng trước khi thực hiện đổi tên.
Xác nhận đã sửa xong
Chạy lại lệnh bị lỗi để xác nhận vấn đề đã được giải quyết:
# Chạy lại lệnh bị lỗi
npm run build
# Hoặc kiểm tra trực tiếp thao tác đổi tên
node -e "require('fs').renameSync('test.tmp', 'test.out'); console.log('OK')"
Nếu lỗi ban đầu xuất phát từ npm install, hãy xóa node_modules và file lock trước khi thử lại — một lần cài đặt chưa hoàn chỉnh có thể để lại các handle cũ:
rd /s /q node_modules
del package-lock.json
npm install
Phòng ngừa
- Loại trừ thư mục dự án khỏi antivirus ngay từ đầu — đừng chờ đến khi lỗi xuất hiện. Đây là thực hành tiêu chuẩn trên máy dev Windows và chỉ mất 30 giây để thiết lập.
- Dùng process manager như PM2 cho các server chạy lâu dài — nó đóng file handle đúng cách khi khởi động lại thay vì tắt tiến trình đột ngột và để lại các khóa mồ côi.
- Ghi file tạm vào
os.tmpdir()khi có thể — các thư mục temp của hệ thống thường được loại trừ khỏi trình quét theo mặc định, nên va chạm khi đổi tên hiếm khi xảy ra. - Tích hợp logic retry vào script production — Windows CI server và ổ đĩa mạng dùng chung đặc biệt dễ gặp khóa tạm thời. Một vòng lặp retry 3 lần không tốn gì và ngăn được các lỗi build ngẫu nhiên.
Nếu dự án của bạn cũng chạy trên Linux và bạn cần xử lý quyền file trên cả hai nền tảng, Unix Permissions Calculator trên ToolCraft giúp bạn tính toán giá trị chmod phù hợp một cách trực quan — không cần nhớ số octal.

