I deleted a file from Explorer, but it came back when I refreshed, and I get Access Denied if I try to delete it again

Raymond Chen

A customer reported that if they go into Explorer and delete a file, the file disappears from the Explorer window, but if they refresh the window, the file comes back. If they try to delete the file again, they get an Access Denied error.

What’s going on?

The Windows model for file deletion is that deleting a file doesn’t take effect until all open handles to the file are closed. When you delete the file from Explorer, the file operation engine performs a Delete­File to delete the file, and the call says “Yup, the file is deleted. Everything is awesome.” But secretly, everything isn’t awesome yet.

The file operation engine is told that the Delete­File was successful, so it sends out a notification that says, “In case anybody cares, I just deleted a file!” The Explorer window receives this notification and removes the file from the view.

When you refresh the window, the Explorer window goes back and enumerates the contents of the directory, and hey look, the file is still there. So it shows back up.

If you try to delete the file a second time, the call to Delete­File fails because the file has already been logically deleted. The file system is just waiting for everybody who was using the file to be done with it.

The customer’s investigation was that the file is indeed still open. The had an application running that had the file on a work list, and to ensure that the work could be completed, the application keeps a handle open to those files until the work is completed (or canceled).

The customer wanted to know if there was a way to make the file really be deleted when the user deletes it, so it doesn’t show up in the Explorer view, because their employees’ workflow is to delete the file when they’ve finished with it, and having the file linger in the view makes it hard to keep track of what is left to be done.

Well, the work isn’t actually done, because it’s still sitting on the application’s work list. They can adjust their workflow so they delete the file when the application completes the work item. Or find a way to delete the work item from the application’s work list. Or they could talk to the application vendor and ask if there’s an option to disable the “keep an open handle to the file” feature, even though it means that when the application goes to work on the file, the file may not be there any more. (Or it might be a different file entirely, just with a coincidentally identical name.)

11 comments

Comments are closed. Login to edit/delete your existing comments

  • Sunil Joshi

    I think I read that this behaviour can be changed by FILE_DISPOSITION_POSIX_SEMANTICS?

    • Joshua Hudson

      If it can I want to know exactly how to do it.

      I took to renaming the file before deleting it in application code because this misfeature stinks to high heaven.

      Now someone’s going to come along and say some application might be depending on the old behavior. Well let them have their old behavior until they’re fed up with it. My code doesn’t need this, nor do the files that my code manipulate because again they’re opened by more of my own code. The only thing that needs this is Windows, and as exhibited by the rename-out-of-the-way working, Windows doesn’t need it either.

      But occasionally the OS crashes while a handle is still open, leaving the renamed file sitting around forever. Booo.

      • Sunil Joshi

        Unfortunately that flag needs to be set by the program or driver doing the delete – I saw this in the file system implementation of the MSVC STL:

        Link to Source.

        • Joshua Hudson

          Doesn’t work. The Windows Executable Loader is still the offender.

      • David Trapp

        You could additionally schedule the file for deletion on reboot.

  • Mystery Man

    I’ve never seen such behavior from File Explorer. It either deletes the file or generates an error message. Sometimes, it even finds out which app is locking the file, preventing its deletion.

  • Valts Sondors

    Is there also a Nuclear Option somewhere? Something like “Close all open handles to this file whether their applications want it or not, BWAHAHAHAHAHA”?

    • Joshua Hudson

      In early 2020 a Visual Studio bug drove me to build said nuclear option. It works reliably unless somebody has a mapped section on the file.

      And no, it doesn’t cause the cascading file handle problem because I did it right. With the target process paused, I closed the handle and opened a handle to NUL in the same slot. This takes a truly annoying amount of code because Win32 doesn’t have int 21h function 46h. Now how the VDM provides this function is anybody’s guess.

      I never could locate all the mapped sections and replace them with swap-backed sections. If there’s a mapped section the operation fails.

  • Sukru Tikves

    The Linux/UNIX way is slightly better.

    They remove the directory entry, but keep the allocation on the file system. When there are zero “hard links” the file is then reclaimed.

    There are strange situations, where a file can be deleted, and another one can be written with the same name. Now there could be two applications open files with the same name but different contents. A very good way to introduce bugs if files are used as locking/synchronization primitives.

    Or the open handle can be used to write a new name while the file is deleted. It will essentially “undelete” the file:
    https://serverfault.com/a/238431

    Of course it is currently not feasible to change existing behavior.

  • Nick

    It seems like the real issue here is showing “Access Denied” when trying to delete a file that has already been logically deleted. I don’t know if that’s the actual error code returned by the filesystem or if Explorer is (mis)interpreting it, but if Explorer showed a message saying “This file is pending deletion and will be removed once all references to it are closed or the system is restarted.” Or throw something in there about “apps”.

    I’ve noticed that Access Denied is a pretty common catch-all error in Win32, essentially having become equivalent to “request failed for some reason”.