Error handling is not an easy task. Even the tiniest bit of code can fail in spectacular ways.
Luckily, modern programming languages tend to use at least some sort of exception model, which means that even if your program crashes, you’ll still be able to obtain more debugging information than a "Segmentation fault" or other generic error message would provide.
Manuel, however, was not so lucky. Having been asked by one of his interns for help on an internal ASP.NET app that "just wouldn’t work", he found himself staring at the most useless error page imaginable:
An error has occurred. Please contact the system administrator.
"I narrowed it down to that bit" – the intern said sheepishly – "but I have no idea what’s wrong with the code. I mean, it should work, right?"
try { count = repository.GetProductCount(); //... } catch (Exception e) { Server.Transfer("error.aspx"); }
"Okay, it looks like there’s some sort of database problem..." – Manuel muttered to himself, diving into the code.
public int GetProductCount() { try { var conn = new DB2Connection(connString); //... } catch (Exception e) { throw new InitrodeException(e.Message); } }
"DB2, DB2... Have you installed the data provider?" – he asked, trying to rule out the horses before looking for zebras.
"Umm..." – the intern’s hesitant mumble pretty much confirmed the diagnosis. Manuel pointed him to the appropriate Confluence site and was getting ready to leave, when something else caught his attention.
There was a single breakpoint in the code, right on the Server.Transfer line.
"Can you run that again?" – he said. The intern clicked "Debug", and was greeted with the same error page as before, with the breakpoint never getting hit.
"Huh... Okay, just run the install." – Manuel said, rushing to his own desk to investigate. He made sure he had the code flow right, then proceeded to mangle his web.config file to get the DB2 provider to throw an exception. As expected, the error page appeared as soon as he clicked "Debug".
He set a breakpoint in the catch block and ran the app, only to instantly be greeted by the error page again. After a quick search through the codebase, he finally found himself staring, jaw wide open, at the most unlikely place...
public class InitrodeException : Exception { protected HttpContext context; public InitrodeException() { } public InitrodeException(string message) { context = System.Web.HttpContext.Current; context.Server.Transfer("error.aspx"); } }
Terrified to his core, he checked a couple of other code files – and of course, all of them followed the same anti-pattern. Almost every possible exception was caught, dismembered, had its message violently extracted and stuffed into the God-exception class, which then discarded it anyway and did a redirect, on its own, in the exception constructor.
Fuming, Manuel checked the version control logs and rushed to the consultant responsible for the changes.
"Hello. Would you maybe care to explain that code?" – he asked, laptop in hand, voice cold and sharp as a knife, but not missing a tiny glimmer of hope that maybe there is some kind of an explanation.
"Oh, that’s our security feature." – the consultant barely glimpsed at Manuel before resuming his work.
"What?"
"Look..."– with a long sigh, the consultant typed up a few words in Visual Studio and turned the monitor. "What do you see here?"
The screen showed a standard ASP.NET error page, with an unhandled exception showing up.
"Uh... that’s... an error page." – Manuel managed to utter, completely thrown off by the question.
"Exactly. And what’s on that page? Code. Stack traces. Line numbers. How can you call a website secure if you expose this kind of data to just anybody? And all that from just one unhandled exception that slips through. But here – " – the consultant tapped at Manuel’s screen – " – we simply let the exceptions handle themselves, which makes the app robust and secure."