Thursday, February 14, 2008

Worst .NET Bug I've Ever Seen

Question: What exception(s) will this code produce? And why?

while (true) {
   using (Stream sw = File.Open(strFileName, FileMode.Create)) {
         using (BinaryWriter bw = new BinaryWriter(sw)) {
            BinaryFormatter bf = new BinaryFormatter();
            bf.Serialize(bw.BaseStream, this);
         }
   }
}


Answer: Well, it seems like it shouldn't produce any exceptions. It should run forever: create a file, write data to it, close the file. Same thing, over and over again.

The usings should ensure that the BinaryWriter and Stream are closed each time through the loop.

But that's not what happens. Run it enough times, and you'll get an exception: System.IO.IOException: Cannot create a file when that file already exists. How can that file be in use? You clearly closed it last time through the loop!

Even stranger: if you follow your using with code that is doing something else with the file (like, say, moving it), you'll occasionally see a System.UnauthorizedAccessException: Access to the path is denied exception. This is a file that you clearly are authorized to access--you just created it!

Note: adding explicit calls to sw.Close() and bw.Close() doesn't change the behavior--you still get exceptions eventually.

This seems like some unholy combination of a problem between .NET, Win32, and the OS, combined with an incorrect exception being thrown sometimes.

Unfortunately, this little nasty reared its head on a customer site. And, naturally, it wasn't tightly packaged like the code above. Occasionally, inexplicably, exceptions were being thrown. It's hard to reproduce in the wild, and it took us a few days to track down and boil down.

Wow. Any .NET experts care to weigh in on why this would throw exceptions?

Watch Robert's blog to see how we ended up fixing this...

Update 2/15/2008 9:12: After a conversation with Robert, I felt I should make it clear: our software didn't have a loop like the one written above; we came up with that when trying to reproduce the behavior. He'll have more details later...

Technorati tags: