Skip to main content

If you don't have an IBM ID and password, register here.

By clicking Submit, you agree to the developerWorks terms of use.

The first time you sign into developerWorks, a profile is created for you. This profile includes the first name, last name, and display name you identified when you registered with developerWorks. Select information in your developerWorks profile is displayed to the public, but you may edit the information at any time. Your first name, last name (unless you choose to hide them), and display name will accompany the content that you post.

All information submitted is secure.

The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerworks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

By clicking Submit, you agree to the developerWorks terms of use.

All information submitted is secure.

Server clinic: Writing good exceptions

Improve your error-handling style

Cameron Laird (claird@phaseit.net), Vice president, Phaseit, Inc.
Cameron is a full-time consultant for Phaseit, Inc. He writes and speaks frequently on open source and other technical topics.

Summary:  Too many developers treat exception systems as they do romantic language: the words are familiar, but they aren't sure what to say with them. Cameron offers practical advice on what your programs should throw and catch.

Date:  22 May 2003
Level:  Intermediate

Comments:  

A refined exception system is one of the most distinctive advantages modern programming languages offer. Many experienced programmers still don't know, though, how to use exceptions well. Or, perhaps more precisely, they don't use them the way I think best. One consequence, among others, is to damage the security of their systems. So, let's see what we can improve.

How to think about exceptions

Textbooks and similar references continue to concentrate on the syntax and local semantics of exceptions. They teach enough that most programmers can read code that employs exceptions and explain its actions. What they're missing is a sense of effective style. To acquire your own,

  • focus on what you want exceptions to do for you,
  • how you should catch them, and
  • how you should toss them.

This month's column provides examples of how to do all three.

Before looking at the examples, though, warm up by thinking about exceptions in a different way from what you perhaps were first taught. The exception system your favorite language gives you is not fit for end users to see. Instead, regard exceptions as scaffolding that you'll remove from view as you complete your application. Maybe you were in a class sometime and learned to read such displays as

caught exception in main()
java.lang.SomeException: ugly input
at ...

This skill is valuable for programmers, of course. It absolutely must not be imposed on end-users, though. A completed application should never speak "exceptionese"; all reports to end-users ought be in the native language of the latter, perhaps something closer to

The configuration file 'folder/thing.cfg'
appears to be corrupt, as line #17 cannot
be parsed.

Clarity of this sort bears directly on the security of an application. Here's why: over and over again, users and their administrators demonstrate that their response to mysteries is to simplify their systems until they get a desired behavior. If they read, "file not found," they'll copy files around freely, without regard to privileges or permissions. One of the surest ways to protect the security of an application is to make it work so that users understand its operation. Clever users can subvert almost any security settings in their hunger to "make it work."

Not all programmers agree with me on this. There are quite a few senior software engineers who soberly advise that "users get what they deserve" when they enter faulty data or otherwise misuse an application. My point is not to argue with the ethics of this stance; it's just to observe that security consistently crumbles when developers and end-users adopt such adversarial stances toward each other.

The first step in using exceptions in a particular development project, therefore, and much the most-neglected one, is to determine your program's requirements for exceptions. Make this explicit. When a client or supervisor dictates how a program should process well-formed input data, take the opportunity to agree in detail on how the program should act in case of error. Give yourself adequate time to interview your client. Take this perspective: your end users might occupy a plurality of their "contact hours" with your program viewing its error messages. That's not as extreme as it might sound, for "normal" operations with many applications are quite quick, while response to errors demands time-consuming human thought. Your error messages and actions merit as much craftsmanship as the rest of your program.

In fact, I've often been happiest with projects where the best talent has concentrated on error-handling rather than the traditionally "sexier" aspects of programming, such as graphical user interface (GUI) appearance. This is the reason: a faulty application with good error-handling seems to leave end-users with more satisfaction and comfort than one that's nearly perfect, but has a hostile error-handler.

With a first round of requirements analysis complete, the narrative notes you have in hand will make design of your program's exception-handling far more rational and rewarding. Now the challenges become the technical ones that this column's readers relish.


Catching

Python is a convenient vehicle to express example usages. One blemish I often encounter looks like this:


Listing 1. Mismatched catch
    try:
        process(some_file)
    except:
        alert("error in opening" '%s' % some_file)

Do you see the problem? The syntax and semantics of the exception are mismatched, somewhat like a public servant who promises to tend to voters' most important concerns, especially the swimming pool service hours. While such a sentence might be formally correct, its imbalance jars the listener, and hints at deeper problems.

The mismatched catch above is similar: it catches all errors, but it reports only that there was an error in "opening." It's better to write this:


Listing 2. Better-balanced catch
    try:
        process(some_file)
    except IOError:
        alert("error in opening" '%s' % some_file)
     

Many programmers have a sincere belief that these two examples are equivalent, because, in one possible argument, "process is documented only to raise IOErrors." At that level, yes, it's true that the application will execute indistinguishably in the two cases. Source code isn't only for the computer, though; more than that, it needs to express meaning to the human reader. If your code assumes that a particular exception must be an IOError, take advantage of the language's precision, and say so.

The second example still fails to protect the end-user adequately from the hazard of seeing "raw" exceptions. In practice, even if process() were documented unambiguously as throwing only IOError in its current version, I'd demand that this segment be coded at least in this level of detail:


Listing 3. Formally balanced comprehensive catch
    try:
        process(some_file)
    except IOError:
        alert("error in opening" '%s' % some_file)
    except:
        alert("internal and completely unexpected problem")
     

Rarely do we have the luxury of working with completely and correctly documented interfaces, of course. One technique that's useful during development work with many languages is to use the exception system's built-in introspection. This turns our example into something like this:


Listing 4. Formally balanced and comprehensive catch, with informative "default"
    try:
        process(some_file)
    except IOError:
        alert("error in opening" '%s' % some_file)
    except:
        (exc_class, exc_object, exc_traceback) = sys.exc_info()
        alert("""internal and completely unexpected problem,
        manifested as %s""" % str(exc_class))

If process()'s implementation left it with a fault that resulted in a ValueError, for instance, rather than an IOError, the last handler above would at least report out ValueError as that class name.

There's a complementary vagueness in catching that is also common. It produces code such as this:


Excessively wide scope in error-handling
    try:
        first_operation()
        second_operation()
        third_operation()
        fourth_operation()
    except:
        alert("something went wrong")

The misstep here is a "horizontal" imprecision: when something went wrong appears, there's no immediate connection to the specific *_operation() that caused it. A straightforward solution is to catch only one segment at a time, so that the previous coding might become:


Listing 6. Better precision in error-handling
        # The documentation assures us these
        #    two can't toss exceptions.
    first_operation()
    second_operation()
    try:
        third_operation()
    except:
        alert("something went wrong in 'third_operation()'")

        # This, also, cannot throw an exception.
    fourth_operation()
     

Slightly more sophisticated is to catch wider spans of code, but to use a language's introspection capabilities to report traceback information:


Listing 7. Many languages can manage their own tracebacks
    try:
        first_operation()
        second_operation()
        third_operation()
        fourth_operation()
    except:
        exc_traceback = sys.exc_info()[2]
        stack_list = []
        while 1:
            stack_list.append(exc_traceback.tb_frame.f_code.co_name)
            if not exc_traceback.tb_next:
                break
            exc_traceback = exc_traceback.tb_next

    # The next is an almost-human-readable
    #    description of where the fault occurred
         alert("something went wrong in %s" % stack_list)

When catching exceptions, then, be certain you get all of them, be precise in how you do so, and use the information available in your chosen language to synthesize useful output.


Throwing

Production of exceptions is a slightly more advanced topic than their consumption. All Server clinic readers should know the basics, though:

  • Document your interfaces.
  • Keep your inheritance hierarchy simple.

Inheritance is a great technique in languages that support it for exception values; it's quite useful to organize faults around such categories as IOError, ValueError, AppError, and so on. Inexperienced designers, however, frequently complexify their inheritance hierarchies far beyond the optimum. If you find yourself defining more than a couple of levels of inheritance in your exception classes, review your work. If you have more than three, or more than seven subclasses of a single exception superclass, my bet is that something is wrong.

One of the mistakes often made in this regard is to replicate under Exception or Error a tree of application objects. This is almost always a mistake, but one that is easily corrected by parametrizing the exceptions rather than subclassing them. A design that specifies MercuryException, VenusException, and so on probably doesn't serve you best; far more likely is that you should code in terms of PlanetException, with a datum attached that specifies which planet is at issue.

A slightly subtler style, one that supports design-by-contract (DbC) approaches, is to renounce definition of any exception classes beyond an AssertionError, then code all exception interfaces in terms of assertions.


Advanced exceptions

There's a great deal more to know about exceptions, very little of it adequately detailed in printed books: guarding exceptions so they can't be used to breach security; exception metrics; performance consequences of exception design; ways to debug, benchmark, and validate exceptions; exceptions and resource management; and much more. One final point to record for this month is the significance for exception systems of multi-language coding.

Server clinic often advocates "dual-level" programming -- combination of a couple of different languages -- to exploit the strengths of each. This has become sufficiently popular and "normal" that there are now quite a few systems that take on, among other chores, that of "converting" objects between one language and another. David Beazley's Simplified Wrapper and Interface Generator (SWIG) is one particularly broadly-used tool for this sort of work.

Neglected until now, though, has been the need to teach the exception systems of different languages to talk to each other effectively. That's a challenge Beazley, now an assistant professor with the Department of Computer Science at the University of Chicago, is tackling. His Wrapped Application Generator (WAD) "is an embedded debugging system that is intended to simplify the task of debugging scripting language extensions". Learn more about WAD, SWIG, and other advanced exception topics, through the Resources listed below. Also, remember to visit the Linux zone's Scripting clinic forum to discuss technical details of exception management.


Resources

  • Check out the other installments of Server clinic.

  • Server clinic in particular and developerWorks in general often mention Python. One particularly important resource for anyone not already familiar with the language is the book Python in a Nutshell, just published by O'Reilly in March 2003. It's a capable if terse introduction to the language, as well as the definitive printed reference to the latest version. Although the book as a whole is streamlined and even laconic, its treatment of exceptions is unique among books on any language in its depth and sage counsel regarding good style.

  • Although less ambitious than Nutshell, the standard Python documentation on Errors and Exceptions is quite clearly written.

  • While How to Think Like a Computer Scientist: Learning with Python delivers the promise of its title quite well, that's faint praise in the case of exceptions. The chapter on that subject conveys just the rather primitive knowledge of exceptions that too many programmers have.

  • It truly is remarkable how little attention exception systems receive in accessible published works. Always appropriate in any review of security, requirements, and quality, though, is Handbook of Walkthroughs, Inspections, and Technical Reviews. I applaud the strength of its message that coding is a "last resort" in solving problems, as well as its willingness to get intimate with errors and their consequences.

  • In contrast, there's an abundance of writing about "Design by Contract" and how to practice it in various languages. At the center is Building bug-free O-O software: An introduction to Design by Contract.

  • David Beazley's Wrapped Application Generator is an interesting project that currently focuses on exposing "segfaults" as "native" exceptions in a higher-level language.

  • Best practices in EJB exception handling (developerWorks, May 2002) discusses how to handle EJB exceptions that go beyond the basics and actually address user needs.

  • EJB best practices: Build a better exception-handling framework explains an exception framework in the context of separating exception handling by tier (developerWorks, January 2003).

  • Mastering Linux debugging techniques (developerWorks, August 2002) outlines a number of tools and strategies that help you to better track down program errors in the first place.

  • The Cranky User: "I can't use this (developerWorks, May 2002) takes a look at how improving usability is often the best form of error prevention.

About the author

Cameron is a full-time consultant for Phaseit, Inc. He writes and speaks frequently on open source and other technical topics.

Report abuse help

Report abuse

Thank you. This entry has been flagged for moderator attention.


Report abuse help

Report abuse

Report abuse submission failed. Please try again later.


developerWorks: Sign in

If you don't have an IBM ID and password, register here.


Forgot your IBM ID?


Forgot your password?
Change your password


By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. This profile includes the first name, last name, and display name you identified when you registered with developerWorks. Select information in your developerWorks profile is displayed to the public, but you may edit the information at any time. Your first name, last name (unless you choose to hide them), and display name will accompany the content that you post.

Choose your display name

The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

(Must be between 3 – 31 characters.)


By clicking Submit, you agree to the developerWorks terms of use.

 


Rate this article

Comments

Help: Update or add to My dW interests

What's this?

This little timesaver lets you update your My developerWorks profile with just one click! The general subject of this content (AIX and UNIX, Information Management, Lotus, Rational, Tivoli, WebSphere, Java, Linux, Open source, SOA and Web services, Web development, or XML) will be added to the interests section of your profile, if it's not there already. You only need to be logged in to My developerWorks.

And what's the point of adding your interests to your profile? That's how you find other users with the same interests as yours, and see what they're reading and contributing to the community. Your interests also help us recommend relevant developerWorks content to you.

View your My developerWorks profile

Return from help

Help: Remove from My dW interests

What's this?

Removing this interest does not alter your profile, but rather removes this piece of content from a list of all content for which you've indicated interest. In a future enhancement to My developerWorks, you'll be able to see a record of that content.

View your My developerWorks profile

Return from help

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Linux
ArticleID=11315
ArticleTitle=Server clinic: Writing good exceptions
publish-date=05222003
author1-email=claird@phaseit.net
author1-email-cc=

Tags

Help
Use the search field to find all types of content in My developerWorks with that tag.

Use the slider bar to see more or fewer tags.

For articles in technology zones (such as Java technology, Linux, Open source, XML), Popular tags shows the top tags for all technology zones. For articles in product zones (such as Info Mgmt, Rational, WebSphere), Popular tags shows the top tags for just that product zone.

For articles in technology zones (such as Java technology, Linux, Open source, XML), My tags shows your tags for all technology zones. For articles in product zones (such as Info Mgmt, Rational, WebSphere), My tags shows your tags for just that product zone.

Use the search field to find all types of content in My developerWorks with that tag. Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere). My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).