System.IO.IOException: "The file exists" when using System.IO.Path.GetTempFileName() - resolutions?

If this is happening to you on a production environment or with an app that you can't change, the quick fix is to empty the Temp folder.

Depending on the user that is running the application you should either

  • Empty C:\Windows\Temp (for IIS or services running under LocalSystem account)
  • Or %temp% for locally logged on users (which for me is C:\Users\MyUserName\AppData\Local\Temp).

On the other side, if your own code is throwing this, and you want to prevent this from happening ever again:

  1. Do not use System.IO.Path.GetTempFileName()!

GetTempFileName() is a wrapper of the two decades old Win32 Api. It generate file names that will very easily collide. It circumvents those collitions by heavily looping on the file system, iterating possible file names from "%temp%\tmp0000.tmp" to "tmpFFFF.tmp" and skipping already existing ones. This is a I/O intensive, slow, and frankly terrible algorithm. Also using only 4 hex characters is what makes the artificial limit of 65536 files before failing.

The alternative is to generate file names that will not collide. For example, lets reuse GUID's logic: 32 hex digits will almost never collide.

private string GetTempFileName()
{
    return Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
}
// Sample: c:\Windows\Temp\2e38fe87-f6bb-4b0d-90b3-2d07016324c1

This expands the limit from 65k to 4k millions files max (theoretically)... Of course, having leaked 65k files is already terrible, so...

  1. Do not leak temp files!

Double check your app for all happy and unhappy paths (like unexpected exceptions). Ensure it's correctly disposing each FileStream and deleting the temp files in Finally blocks .

  1. Clean the temp folder

Clean it now, and educate the system administrator to clean it periodically, because you can't trust every app in the wild. On my own servers I would automate this task using:

  • For global Windows\Temp

schtasks /Create /TR "cmd /c call DEL /F /S /Q %^TEMP%" /TN "Delete Global Temp Files" /sc WEEKLY /ST 12:00 /ru system

  • For current user:

schtasks /Create /TR "cmd /c call DEL /F /S /Q %^TEMP%" /TN "Delete %username% Temp Files" /sc WEEKLY /ST 12:00


As I mentioned in my last comment I think your only safe way to do this is to ask the user if they want you to delete files and try again. It is imperative that you get the users input into this, this way it is at their own peril. In my head its something similar to.

public Stream GetStream(Stream cursorStream)
{
    try
    {
       //getting stream
    }
    catch(IOE)
    {
        MessageBox.Show(this, "Unable to get stream, your temporary
                              folder may be full, do you want to try deleting 
                                some and try again?");
         if(yes)
         try
         {
             //delete and try again
             return GetStream(cursorStream);
         }
         catch(IOE)
          {
                //no luck
           }
          else
              return null;
    }

}

An optional check to make sure could be,

Directory.EnumerateFiles(Path.GetTempPath(), "*", SearchOption.TopLevelOnly)
  .Count() == ushort.MaxValue;

Here's the code I used in the end, and put early in my app's initialization code-path, before any calls to Cursor.LoadFromStream might occur:

    private void WarnUserIfTempFolderFull()
    {
        string tempFile = null;
        try
        {
            tempFile = Path.GetTempFileName();
        }
        catch (IOException e)
        {
            string problem = "The Temporary Folder is full.";

            string message = "{ProductName} has detected that the Windows Temporary Folder is full. \n" + 
                             "This may prevent the {ProductName} from functioning correctly.\n" + 
                             "Please delete old files in your temporary folder (%TEMP%) and try again.";

            Logger.Warn(problem);

            MessageBox.Show(message, caption: problem);
        }
        finally
        {
            if (tempFile != null) File.Delete(tempFile);
        }
    }

Tags:

C#

.Net