unsafeCleanup fails on windows
rDarge opened this issue · comments
Operating System
- Linux
- Windows 7
- Windows 10
- MacOS
NodeJS Version
- 17
Tmp Version
0.2.1
Expected Behavior
I expect node-tmp to recursively delete contents of the folder and then the folder itself
Experienced Behavior
node-tmp appears to delete all of the contents of the folder and then error, with the following stack trace:
Error: ENOTEMPTY: directory not empty, rmdir 'C:\Users\Me\AppData\Local\Temp\tmp-1920-PmZ4Htrvkfa6'
at Object.rmdirSync (node:fs:1167:10)
at rmkidsSync (C:\Users\Ryan\Documents\Github\kai-backend\node_modules\rimraf\rimraf.js:349:27)
at rmdirSync (C:\Users\Ryan\Documents\Github\kai-backend\node_modules\rimraf\rimraf.js:329:7)
at rimrafSync (C:\Users\Ryan\Documents\Github\kai-backend\node_modules\rimraf\rimraf.js:301:9)
at Object._cleanupCallback [as removeCallback] (C:\Users\Ryan\Documents\Github\kai-backend\node_modules\tmp\lib\tmp.js:356:16)
at Object.<anonymous> (C:\Users\Ryan\Documents\Github\kai-backend\routes\working-folder.ts:24:15)
at Generator.next (<anonymous>)
at fulfilled (C:\Users\Ryan\Documents\Github\kai-backend\routes\working-folder.ts:5:58)
at processTicksAndRejections (node:internal/process/task_queues:96:5)
Attempting to remove the directory using unsafeCleanup: false results in nearly identical behavior:
Error: ENOTEMPTY: directory not empty, rmdir 'C:\Users\Ryan\AppData\Local\Temp\tmp-18508-Ogmop5DSalF4'
at Object.rmdirSync (node:fs:1167:10)
at Object._cleanupCallback [as removeCallback] (C:\Users\Ryan\Documents\Github\kai-backend\node_modules\tmp\lib\tmp.js:356:16)
at Object.<anonymous> (C:\Users\Ryan\Documents\Github\kai-backend\routes\working-folder.ts:24:15)
at Generator.next (<anonymous>)
at fulfilled (C:\Users\Ryan\Documents\Github\kai-backend\routes\working-folder.ts:5:58)
at processTicksAndRejections (node:internal/process/task_queues:96:5)
Looking at the rimraf.js source code in the area where the exception was thrown, I see:
// We only end up here once we got ENOTEMPTY at least once, and
// at this point, we are guaranteed to have removed all the kids.
// So, we know that it won't be ENOENT or ENOTDIR or anything else.
// try really hard to delete stuff on windows, because it has a
// PROFOUNDLY annoying habit of not closing handles promptly when
// files are deleted, resulting in spurious ENOTEMPTY errors.
I've slapped together a fix by retrying "removeCallback()" if it fails the first time.
try {
dir.removeCallback();
} catch (er) {
console.log("could not remove temp directory, trying again", er);
//Will often fail the first time on windows, not so often the second time.
dir.removeCallback();
}
@rDarge I am wondering, do you have async code that is still operating on a file inside the temporary folder?
If that is the case, then this might be a race condition...
Hey @silkentrance ! Thanks for checking in. All async code should have resolved by the time I removed the callback; I remember looking into it at the time and arrived at the conclusion that windows sometimes hangs on to recently opened files briefly after a process releases a lock on them...
our prod machine is running linux and hasn't had any issues with this though
@rDarge Thanks for the quick feedback. Windows, sigh. Maybe relying on CI tests run in a target platform environment instead of relying on locally run tests will do the trick.
I would not know on how to deal with this issue, where Windows is delaying the deletion of the file lock to after that a specific process terminates...
See the workaround provided by @rDarge.
Would it be possible to document this behaviour somewhere? I though I was going crazy when my tests were passing on macOS and CI (Linux) but not on Windows.