in reply to The art of error handling

I usually die when either the error is obviously fatal or is not supposed to happen at all (if it happens then there is a bug in the program).

Otherwise I return a false (0) or undef value. False is nicer to test than undef but it is not always possible as 0 might be a valid returned value.

If undef is also a valid returned value then I am in trouble ;--) I try to code around it though, making it an illegal result.

Replies are listed 'Best First'.
Re: Re: The art of error handling
by AgentM (Curate) on Dec 19, 2000 at 21:54 UTC
    Since you mentioned CGI (thus, CGI app writing), it is extremely important to return some error code and message to the user/browser (this is called "user-friendliness"). This is not so intuitive until you use CGI::Carp or better yet, you own personalized error message system which fits in with the theme of the rest of the site. The problem I see with global variables is that they become to cluttery and are named differently for every module. I find function return codes far more useful and clear. Plus, you can put the whole shebang in the same statement!
    die "Oops!\n" unless evilfunction(); #or "if", if you prefer
    I suppose the use of the global vars carried over from C when you were basically stuck returning static variables for multi-variant-byte length information. Of course, Perl does not have this restriction and thus would recommend using returns. My favorite scheme is 0 for success, some string or number on failure- preferably something I might be able to output to the user. This is definitely that something that has come with me from my UN*X C background (POSIX likes to make success 0 now).
    AgentM Systems nor Nasca Enterprises nor Bone::Easy nor Macperl is responsible for the comments made by AgentM. Remember, you can build any logical system with NOR.
      Since you mentioned CGI (thus, CGI app writing), it is extremely important to return some error code and message to the user/browser (this is called "user-friendliness"). This is not so intuitive until you use CGI::Carp or better yet, you own personalized error message system which fits in with the theme of the rest of the site.
      But don't be too friendly. Never tell anything about why an error happened. Just say "something broke, we already know about it", no matter what went wrong. Do not say "database error" or "invalid URL" or anything that reveals any kind of nature about why.

      Yes, it's human nature to want to know (and to reveal), but it's a evil person's nature to use that information to reveal potential breakin paths. So don't. Just say no to "helpful error messages".

      -- Randal L. Schwartz, Perl hacker

        Good point, VERY true with CGI apps, to use something like "...or die('Failed to open $file');" would reveal the path to a critical file to potential trouble-makers.

        Hot Pastrami

        Yep! A typical example, is to have the same error message when a user has an invalid login and when they used a legal login but their password is invalid. Don't let them know that they guessed a login.

      My personal opinion is to never, ever, ever die when using CGI.

      The most annoying thing to me (as a web user) is when something doesn't work and I'm just presented with an error. That pisses me off more than anything else. If I get an irrecoverable error (very, very rare), I want to see an error page that gives me options for notification, ETA on fixes, or other things I can do with my data that I just spent 15 minutes filling out.

      And just to digress for just a moment, 404 pages suck. If the page isn't found, it's not there, but just don't leave me hanging, give me options or redirect somewhere. I went once to a lecture on usability, and while the lecture sucked, the speaker made one good point; that is that as programmers, we spend far too much time just quitting out of apps when something goes wrong. More care needs to be taken in handling the error. Sending an error message back to the user doesn't actually "handle" the error. It just passes it on for the end-user to handle, often times with no options whatsoever.

      The system I have tried to use across the Web site I control, and which I have found particularly useful for DBI work, is:

      return($success_ref, $error_ref);

      Each subroutine returns either

      $dbh->prepare($query) || return (0, "Error: " . $dbh->errstr);

      or

      return ($array_ref, 0);

      You can pepper these throughout your code anywhere there's a DBI action able to fail and it ensures that you can fail somewhat gracefully.

      It's not perfect, but it also makes error-checking easy (just test whether $errors contains anything) and allows you to develop a cascading series of calls where it doesn't bother executing subsequent queries if one has already failed.

Re: Re: The art of error handling
by Hot Pastrami (Monk) on Dec 19, 2000 at 21:51 UTC
    But if you return undef or 0, the error is unspecific, it just means there was a failure... how do you address the specifics? I have been struggling with error checking myself, so I am quite curious what responses will appear.

    Update: By the way, the way that I have been handling errors in modules is to have a sub called error() which takes a string, stores it in an ERRORS array in the package, and return undef, so I can have code like this:
    open(TEST, ">$file") or return $self->error("Failed to open $file");
    That way, I get the undef return value, and store the error in the array in one shot. There's probably a few better ways, though.

    Hot Pastrami

      If you use subroutine/methods which perform simple operations then the calling code usually either ignores the error, fall back to another method ( $val= $obj->method || default) or dies quickly.

      So I usually handle errors at the level just above the method/subroutine where it happens. Not quite the "detect at the lowest level and at the highest level" philosophy, I admit, but it usually works for me.