Coding Horror

programming and human factors

What's worse than a Bad Error Message?

I'm sure I don't have to explain what is wrong with error messages like this:

Catastrophic Failure
General Protection Fault
Error: The operation completed successfully.
But as bad as those are, they pale in comparsion to what is, hands down, the worst kind of error message: a beautiful, well-formatted, informative, incorrect error message.

Due to the issue documented in my previous post, we're currently replacing the database layer of our production application-- switching from Microsoft's System.Data.OracleClient, to Oracle's Oracle.DataAccess. Just what you want to do in a production system, make sweeping changes in the back end soon after deployment. Er, right. But I digress.

The initial conversion went better than expected, and ran fine on development machines within a few hours. However, when we deployed our Smart Client app, we encountered the following exception:

(Inner Exception)
Exception Source:      Oracle.DataAccess
Exception Type:        System.DllNotFoundException
Exception Message:     Unable to load DLL (OraOps9.dll).
Exception Target Site: GetRegTraceInfo
---- Stack Trace ----
Oracle.DataAccess.Client.OpsTrace.GetRegTraceInfo(TrcLevel As UInt32&)
CrazyApp.Loader.EXE: N 00000
Oracle.DataAccess.Client.OracleConnection..ctor()
CrazyApp.Loader.EXE: N 00032
SharedUtils.DB.DBDataset..ctor(info As SerializationInfo, context As StreamingContext)
CrazyApp.Loader.EXE: N 00040
(Outer Exception)
Exception Source:      mscorlib
Exception Type:        System.Reflection.TargetInvocationException
Exception Message:     Exception has been thrown by the target of an invocation.
Exception Target Site: HandleReturnMessage
---- Stack Trace ----
System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(reqMsg As IMessage, retMsg As IMessage)
CrazyApp.Loader.EXE: N 00264
System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(msgData As MessageData&, type As Int32)
CrazyApp.Loader.EXE: N 00682
CrazyApp.API.UserManager.GetUser(dsUser As DataSet&)
CrazyApp.Loader.EXE: N 00000
CrazyApp.UI.Data.ClientDatasetManager.GetCurrentUserDataset(blnForceRefresh As Boolean)
CrazyApp.Loader.EXE: N 00081

Thus began an entire day of hair-pulling exercises in determining why the remoted Oracle call can't locate OraOps9.dll. It has to be a configuration problem on the server with the Oracle driver. Just like the nicely formatted error message says, with its informative stack traces and exception details. Right?

Wrong. After exhausting every possible scenario-- I wish I could say it was skill, but it's a lot more like dogged trial and error-- we determined that, despite the fact that the exception is wrapped in a remoting call, the required file is missing from the client!

I discovered this on my own machine. Intellectually, I knew there was no way I could be getting different results from a server call than any other client. The only possible explanation was a new client dependency introduced by referencing types in Oracle.DataAccess. But I still refused to believe this. In fact, I did not believe it until I duplicated it, by installing the Oracle 9 client and .NET layer on a clean build machine. Sure enough, the smart client app ran fine as soon as I did that.

I've probably spent more time chasing down erroneous error messages than the time I've spent on all other error messages combined. Evidently computers, like people, are big fat stinkin' liars!

Written by Jeff Atwood

Indoor enthusiast. Co-founder of Stack Exchange and Discourse. Disclaimer: I have no idea what I'm talking about. Find me here: http://twitter.com/codinghorror